GoLang之defer底层系列二(v 1.13)

defer1.13:先提个30%

注:本文以Go SDK v1.13进行讲解

Go1.13中defer性能的优化点,主要集中在减少defer结构体堆分配。我们通过一个例子,看看它是怎样做到的。


func A() {
    defer B(10)
    // code to do something
}
func B(i int) {
    //......
}

像上面这样一段代码,在Go1.13中编译后的伪指令是这样的:


func A() {
    var d struct {
        runtime._defer
        i int
    }
    d.siz = 0
    d.fn = B
    d.i = 10
    r := runtime.deferprocStack(&d._defer)
    if r > 0 {
        goto ret
    }
    // code to do something
    runtime.deferreturn()
    return
ret:
    runtime.deferreturn()

注意上面的结构体d,它由两部分组成,一个是runtime._defer结构体,一个是传给defer函数B的参数。它们被定义为函数A的局部变量,执行阶段会分配在函数栈帧的局部变量区域。接下来的runtime.deferprocStack则会把栈上分配的_defer结构体注册到defer链表。通过这样的方式避免在堆上分配_defer结构体。

在这里插入图片描述

值得注意的是,1.13版本中并不是所有defer都能够在栈上分配。循环中的defer,无论是显示的for循环,还是goto形成的隐式循环,都只能使用1.12版本中的处理方式在堆上分配。即使只执行一次的for循环也是一样。


//显示循环
for i:=0; i< n; i++{
    defer B(i)
}
......

//隐式循环
again:
    defer B()
    if i<n {
        n++
        goto again
    }

所以Go1.13中,runtime._defer结构体增加了一个字段heap,用于标识是否为堆分配。

type _defer struct {
    siz       int32
    started   bool
    heap      bool       //标识是否为堆分配
    sp        uintptr
    pc        uintptr
    fn        *funcval
    _panic    *_panic
    link      *_defer
}

defer函数的执行在1.13中没有变化,依然通过deferreturn实现,依然需要把_defer结构体后面的参数与返回值空间,拷贝到defer函数的调用者栈上。只不过不是从堆上拷贝到栈上,而是从栈上的局部变量空间拷贝到参数空间。

在这里插入图片描述

1.13版本的defer减少了_defer结构体的堆分配,但是仍然要使用defer链表。官方提供的性能优化在30%左右,那1.14版本又做出了怎样的优化呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoGo在努力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值