使用 go1.12
考虑以下代码片段:
func main() {
lit := []interface{}{
0, 0, 0, 0, 0,
}
type ifaceHeader struct{ Type, Data unsafe.Pointer }
for i := range lit {
fmt.Printf("%+v\n", *(*ifaceHeader)(unsafe.Pointer(&lit[i])))
}
}
这将输出:
{Type:0x4962e0 Data:0x4c95c0}
{Type:0x4962e0 Data:0x4c95c8}
{Type:0x4962e0 Data:0x4c95d0}
{Type:0x4962e0 Data:0x4c95d8}
{Type:0x4962e0 Data:0x4c95e0}
这表明每个带框的整数都有自己的堆分配元素(因为每个元素的数据指针都不同)。似乎编译器应该能够重用相同值的带框元素。
此外,我希望这种方法适用于具有相同底层类型的不同类型。因此,我还希望:
type EnumC int32
type EnumB int32
type EnumC int32
lit := []interface{EnumA(0), EnumB(0), EnumC(0)}
共享相同的带框元素,因为这三个类型都是 int32
类型。
\cc @randall77
5条答案
按热度按时间7gyucuyw1#
每个接口数据项的后备存储目前是其自身的静态临时变量。由于我们不写入这些静态临时变量,所以共享它们是可以的。为了实现这一点,我们需要使用它们的内容来命名它们,以便链接器可以将它们合并。这应该不难;我认为我们已经为字符串的后备存储这样做了。
顺便说一下,对于这个特定情况,我们可以使用零值(runtime.zeroVal)来代替。
sqougxex2#
这表明每个整数都在自己的堆分配元素中。顺便说一下,这些不是堆分配的元素,而是静态分配的元素(在数据段中)。
ix0qys7i3#
另一个观察。这里是一段汇编代码:
我们正在耐心地填写
lit
,一次填写一个字。对于某些大小和类型的
lit
,我们可以改为布局整个数据结构(多次复制类型字),然后执行 DUFFCOPY 或类似操作将其复制到堆栈中。它编译速度更快,执行速度可能更快,二进制文件大小也许更小。nnvyjq4y4#
Ah,@randall77 在 #29573 (评论)中提出了同样的建议。
相关链接:#21561 , #19818 。
由于我们不写入这些静态临时变量,所以共享它们是可以的。
顺便说一下,我在 https://go-review.googlesource.com/c/go/+/42170/ 中尝试了这个方法,但遇到了问题。所以如果有人想解决这个问题,这是一个起点。
bkhjykvo5#
作为附注,我们可以在这个特定的情况下使用零值(runtime.zeroVal)。这是 #23948 。我们不断发现这一整套问题和可能的解决方案。我们只是没有解决它们。:P