$ go version
go version devel +142a76530c Tue Mar 9 22:00:50 2021 +0000 linux/amd64
type Bytes []byte
func (e Bytes) S() string {
return string(e)
}
根据上述类型定义,我本期望将 x.S()
的Map查找编译为与 string(x)
相同的代码,但实际上并非如此。
也就是说,以下 lookup1
函数比 lookup2
慢,而它本不应该如此:
func lookup1(m map[string]struct{}, name Bytes) bool {
_, ok := m[name.S()]
return ok
}
func lookup2(m map[string]struct{}, name Bytes) bool {
_, ok := m[string(name)]
return ok
}
以下是基准测试中的代码: https://play.golang.org/p/uw-AHzeos-v
以下是基准测试结果:
direct conversion: 6883083 174.4 ns/op 1024 B/op 1 allocs/op
conversion in method call: 27336484 44.18 ns/op 0 B/op 0 allocs/op
6条答案
按热度按时间o8x7eapl1#
cc @josharian
am46iovg2#
/cc @randall77
vm0i2vca3#
我认为这是因为在源代码中需要
m[string(b)]
来触发。在内联后,它看起来更像:由于此优化是在walk中完成的,很难确定(1)所使用的索引是一个转换为字符串的字节切片,以及(2)在字符串转换和Map索引之间没有修改字节切片。(2)在这个编译器的阶段尤其困难。
所以虽然我认为这可能是可以解决的,但肯定需要大量的工作。要么将新信息引入walk,要么将此优化移至SSA,在那里更容易获取这些信息。不确定这样做是否值得这么多努力。
yptwkmov4#
感谢randall77对此进行调查。您认为这个问题应该移到哪个状态?
jhiyze9q5#
FWIW,当我遇到这个问题时,我感到非常惊讶(实际代码中的问题是返回一个按值结构中的字段的字符串值,这样定义是为了防止意外地在
[]byte
和[]byte
之间进行转换。碰巧,该字段被导出,但我原本打算取消导出它,这意味着在Map访问中使用显式类型转换是不可能的)。我假设内联函数与表达式完全等价,导致了显著的性能下降。
我认为在SSA级别进行优化会更好(并可能为其他优化打开大门,例如将
[]byte
传递给不需要复制的字符串参数函数),但也许那会太费劲?vktxenjb6#
虽然不完全相关,但对 #29095 的仔细实现也可以帮助解决这个问题。
在引用的问题中,我们需要将常量字面量注入内联函数中,以便在内联之后使 const string 优化生效。
只是在这里发布一下,以创建跨问题链接。