内联和输出二进制大小

时间:2017-12-25 13:11:22

标签: go

package main

type TreeCell struct {
    Tabs func() *string
}

func Cell() *string {
    s:= ""
    return &s
}

func Table(Line *[]TreeCell) {
    if Line != nil {
        Num["rtt"] = Line
    }
}

var (
    Num map[string]*[]TreeCell 
)

func main() {

    Table(&[]TreeCell{
        TreeCell{Tabs: Cell},
        TreeCell{Tabs: Cell},
        ...repeat 15000 times
        TreeCell{Tabs: Cell},
    })
}

go build -a -v -gcflags" -N -l" -ldflags" -s -w"

可执行文件的大小1,9Mb

__text              1459891   16781312
__rodata             158107   18241216
Total               1951521

如果我将func() *string更改为interface{}

type TreeCell struct {
    Tabs interface{}
}

然后是可执行文件32Mb的大小

__text               1864389   16781312
__rodata            30375699   18645728
Total               32698219

为什么?

转到版本1.9.2

1 个答案:

答案 0 :(得分:0)

interface{}变量的大小为8或16个字节(取决于体系结构为32或64位),函数变量的大小为4或8个字节。所以只有2乘法不能解释输出二进制的巨大差异(15.000 * 8字节只有120 KB)。

您遇到的是不同的内联编译器优化的结果。函数Cell()非常简单,有资格进行内联。

禁用内联时

如果我们在示例中包含-gcflags '-N -l'参数(这些标志告诉编译器禁用内联),则不会内联对Cell的引用,因此使用func() *string只会使用一个4字节的函数指针。

然而,使用interface{}会导致内联 Cell值。接口值包含副本,并且函数调用未内联,但是当隐式包装在interface{}值中时使用函数值时,它会内联(重复)。在每次,即15.000倍!所以基本上Cell()函数体包含15.000倍。现在这很大,这就是生成二进制30 MB的原因。

启用内联时

如果我们排除-gcflags '-N -l'参数,它实际上是相反的:使用interface{}时编译的二进制文件大约为2 MB,使用{{时大约为30 MB 1}}。

这次在复合文字中使用func () *string函数值初始化Cell字段时,编译器将在所有15.000次内联TreeCell.Tabs函数!使用Cell()时,函数体将不会内联。不确定为什么,一个可能的解释是它在interface{}的情况下也被内联,并且因为接口值是不可变的,所以相同的值被使用了15.000次。