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
答案 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次。