Go explore language defer sentence of three mechanisms

Golang version 1.13 and version 1.14 to defer conducted two optimized so that performance overhead defer in most scenes have been significantly reduced, which in the end went through what principle?

This is because these two versions of defereach added a new mechanism, making the deferstatement at compile time, the compiler will be based on different versions of the circumstances of each deferdifferent selection mechanisms to more lightweight run calls.

Allocated on the heap

In the previous 1.13 version Golang, all deferare allocated on the heap, the mechanism at compile time will be two steps:

  1. In the deferposition of the insert statement runtime.deferproc, when executed, will invoke the delay is stored as a _deferrecord, and the address entry and its parameters are called delayed copy saved, stored in the call list Goroutine.
  2. Before the function returns the position of insertion runtime.deferreturn, when executed, will fetch and execute call delay Goroutine from the list, the plurality of delays places continuously performed jmpdefer call is called tail recursion.

Present in every major performance problem with this mechanism of defermemory allocation when generating a record statements, and call overhead system parameters and complete the call recording parameters move.

Allocated on the stack

Go 1.13 version of the new entrants to deferprocStackachieve a form of distribution in the stack to replace deferproc, compared to the latter, allocated on the stack after the function returns _deferwould be released, eliminating the performance overhead generated when the memory allocation, only proper maintenance _deferof the list that is can.

The compiler has its own logic to choose to use deferprocor deferprocStack, in most cases will use the latter, the performance will increase about 30%. But in the deferstatement appears in a loop, the higher order or can not be performed when the compiler optimizations, or by using a function in much the same defertime, it may still use deferproc.

Open coding

Go 1.14 version continue to join the development of coding (open coded), the delay mechanism will be called directly inserted before the function returns, eliminating the need for run-time deferprocor deferprocStackoperation at runtime deferreturnwill not tail-recursive calls, but directly in a loop through all the delay function execution.

This mechanism makes deferthe cost is almost negligible , the only information that is stored in the runtime costs involved in delaying the call, but the use of this mechanism requires a number of conditions:

  1. Not disable compiler optimizations, that is not set -gcflags "-N";
  2. The function of deferthe number of no more than 8, the product of the delay and the number of return statement statement is not more than 15;
  3. defer Not in the loop statement.

The mechanism also introduces an element - delay bits (the defer 'bit) , recorded for each run-time deferwhether or not is performed (especially conditional branch defer), to facilitate determining which of the last delay call function is executed.

Principle delay bits:
within the same function for every one it deferwill be assigned a bit, if it is to be executed is set to 1, otherwise set to 0, the function returns when necessary to determine before reaching the delayed call, use masks to determine each bit position, if the delay is a function call, otherwise skip.

For lightweight, the official will be delayed a bit limited to a byte, or 8 bits, which is why no more than eight deferreasons, if it exceeds still choose to stack allocation, it is clear that in most cases not more than eight.

Use the following code demonstrates:

deferBits = 0  // 延迟比特初始值 00000000

deferBits |= 1<<0  // 执行第一个 defer,设置为 00000001
_f1 = f1  // 延迟函数
_a1 = a1  // 延迟函数的参数
if cond {
    // 如果第二个 defer 被执行,则设置为 00000011,否则依然为 00000001
    deferBits |= 1<<1
    _f2 = f2
    _a2 = a2
}
...
exit:
// 函数返回之前,倒序检查延迟比特,通过掩码逐位进行与运算,来判断是否调用函数

// 假如 deferBits 为 00000011,则 00000011 & 00000010 != 0,因此调用 f2
// 否则 00000001 & 00000010 == 0,不调用 f2
if deferBits & 1<<1 != 0 {
    deferBits &^= 1<<1  // 移位为下次判断准备
    _f2(_a2)
}
// 同理,由于 00000001 & 00000001 != 0,调用 f1
if deferBits && 1<<0 != 0 {
    deferBits &^= 1<<0
    _f1(_a1)
}

to sum up

Past performance problems Golang defer sentence has been criticized recently released version 1.14 of this dispute is finally painted on a phased period. If not in special circumstances, we do not need to care about the performance overhead defer.

Reference material

[1] Ou Changkun - Go in the original language:
https://changkun.de/golang/zh-cn/part2runtime/ch09lang/defer/

[2] Feng Yun on her - go1.14 achieve defer performance improved significantly principle:
http://xiaorui.cc/archives/6579

[3] 34481-opencoded-defers:
https://github.com/golang/proposal/blob/master/design/34481-opencoded-defers.md


This article belongs to the original, starting in micro-channel public number " for Life program ," the background for reprint please leave a message.

After the reply concerned the following information for more resources to
respond to [information] to obtain Python / Java and other learning resources
reply [plug-in] get reptile commonly used Chrome plugin
reply [almost] get to know the latest simulation know almost Log

Guess you like

Origin www.cnblogs.com/zkqiang/p/12389420.html