go cmd/compile: map lookup with result of inlined method cannot use fast path

insrf1ej  于 6个月前  发布在  Go
关注(0)|答案(6)|浏览(43)
$ 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
vm0i2vca

vm0i2vca3#

我认为这是因为在源代码中需要m[string(b)]来触发。在内联后,它看起来更像:

tmp1 := name
tmp2 := string(tmp1)
_, ok := m[tmp2]

由于此优化是在walk中完成的,很难确定(1)所使用的索引是一个转换为字符串的字节切片,以及(2)在字符串转换和Map索引之间没有修改字节切片。(2)在这个编译器的阶段尤其困难。
所以虽然我认为这可能是可以解决的,但肯定需要大量的工作。要么将新信息引入walk,要么将此优化移至SSA,在那里更容易获取这些信息。不确定这样做是否值得这么多努力。

yptwkmov

yptwkmov4#

感谢randall77对此进行调查。您认为这个问题应该移到哪个状态?

jhiyze9q

jhiyze9q5#

FWIW,当我遇到这个问题时,我感到非常惊讶(实际代码中的问题是返回一个按值结构中的字段的字符串值,这样定义是为了防止意外地在[]byte[]byte之间进行转换。碰巧,该字段被导出,但我原本打算取消导出它,这意味着在Map访问中使用显式类型转换是不可能的)。

我假设内联函数与表达式完全等价,导致了显著的性能下降。

我认为在SSA级别进行优化会更好(并可能为其他优化打开大门,例如将[]byte传递给不需要复制的字符串参数函数),但也许那会太费劲?

vktxenjb

vktxenjb6#

虽然不完全相关,但对 #29095 的仔细实现也可以帮助解决这个问题。
在引用的问题中,我们需要将常量字面量注入内联函数中,以便在内联之后使 const string 优化生效。
只是在这里发布一下,以创建跨问题链接。

相关问题