内联和输出二进制大小

问题描述 投票:0回答:1
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

go
1个回答
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,使用func () *string时大约为30 MB。

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

© www.soinside.com 2019 - 2024. All rights reserved.