Go版本
go1.22
在你的模块/工作区中,go env
的输出是什么?
GOARCH=amd64
GOOS=linux
你做了什么?
编译以下程序:
package main
type key struct {
u16 uint16
u8 bool
u32 uint32
}
var map1 map[key]struct{}
var map2 map[uint64]struct{}
var key1 = key{}
var key2 = uint64(0)
var sink struct{}
func main() {
sink = map1[key1]
sink = map2[key2]
}
你看到了什么发生?
编译器输出:
0x0023 00035 (main.go:17) CALL runtime.mapaccess1(SB)
...
0x0040 00064 (main.go:18) CALL runtime.mapaccess1_fast64(SB)
你期望看到什么?
由于 key
类型小于一个uint64,所以希望在两者上都使用 runtime.mapaccess1_fast64
。
然而,这其中存在一些复杂性。例如,通过 unsafe
,有人可能会写入值到 bool
字段和 uint32
之间的填充空间。此外,使用 unsafe
的人可能会写入一个不是0或1的位模式到布尔值中。
我不确定这是可以依赖的事情。
然而,即使我们使 key
结构恰好为64位宽,没有填充且没有布尔值,它仍然不使用 runtime.mapaccess1_fast64
。
6条答案
按热度按时间ycl3bljg1#
cc @randall77,这可能与#66413有关?
k5ifujac2#
不直接相关,没有。
我们非常小心地不在填充中包含数据作为平等(因此哈希)的一部分。我不知道是否有人真正利用了这一点,但我不确定我们是否希望在这一点上改变它。这个假设可能已经在几个地方被烘焙出来了(例如,初始化栈变量的代码可能不会将这些字段当前设置为零)。
然而,即使我们使键结构恰好为64位宽,没有填充和布尔值,它仍然不使用runtime.mapaccess1_fast64。对我来说是这样。即使有布尔值。只有填充会导致问题。如果你有一个可以复现的问题,请仔细检查并发布一个复现示例?
4ktjp1zp3#
你能检查一下并发布一个复现吗?
啊,你是对的。我当时用的是:
虽然加起来是64位,但我忘记了对齐。
flmtquvp4#
由于mapaccess1_fast64通过寄存器传递键,并且我们知道在应用形状时类型中填充孔的位置,因此我们总是可以在调用运行时之前加载值并使用掩码将任何填充字节置零。
hujrc8aj5#
当然,这是个好主意。
dgiusagp6#
一个复杂情况:mapacess1_fast64使用uint64
==
来比较键。因此,不仅mapacess1_fast64
需要进行掩码操作,插入操作也需要进行掩码操作。