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},
})
}
去建立-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版
最佳答案
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
字段时,编译器将内联Cell()
函数所有15.000次!当使用interface{}
时,将不会内联函数体。不知道为什么,一个可能的解释是,在interface{}
的情况下,它也是内联的,并且由于接口值是不可变的,因此相同的值被使用了15.000次。
关于go - 内联和输出二进制大小,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47968970/