go cmd/compile:循环防止边界检查消除

rbl8hiat  于 2个月前  发布在  Go
关注(0)|答案(4)|浏览(27)

你正在使用哪个版本的Go(go version)?

$ go version
go1.14

这个问题在最新版本中是否会重现?

是的

你做了什么?

如果我在循环中对数组进行一些复制操作,例如:

func Convert(a *[32]byte, b *[4]uint64) {
	for i := 0; i < 4; i++ {
		binary.LittleEndian.PutUint64(a[i*8:], b[i])
	}
}

它会在每次迭代时对每个数组进行边界检查。但是如果我展开它,它可以推断出永远不会超出边界,并消除检查:

func Convert(a *[32]byte, b *[4]uint64) {
		binary.LittleEndian.PutUint64(a[:], b[0])
		binary.LittleEndian.PutUint64(a[8:], b[1])
		binary.LittleEndian.PutUint64(a[16:], b[2])
		binary.LittleEndian.PutUint64(a[24:], b[3])
}

go-playground: https://godbolt.org/z/E3c5r4
这意味着当我实现性能关键的代码(例如加密)时,我发现自己展开8-16层的循环,因为它们最终会出现在pprof上,这对于可读性真的很糟糕

oiopk7p5

oiopk7p51#

我们目前不会通过乘法传播已知范围。在prove.go中实现应该不难。

uz75evzq

uz75evzq2#

https://golang.org/cl/248701提到了这个问题:cmd/compile: add support for Lsh64*64 and Mul64 to IsSliceInBounds

xesrikrc

xesrikrc3#

另一个密码学实现者加入讨论!很高兴看到这个改进,理想情况下,它也可以用于除乘法之外的其他操作。它将特别允许我在不展开循环的情况下提高代码的可读性,而不会出现明显的(在一个任务上约30%的性能损失,参见gtank/blake2#2(评论))性能损失。
我努力保持这个库是纯Go语言编写的,这使得在生产部署中,我更有可能需要引入汇编来实现算法本身所期望的吞吐量(cc @FiloSottile)。

3b6akqbq

3b6akqbq4#

https://go.dev/cl/599256提到了这个问题:cmd/compile: propagate constant ranges through multiplies and shifts

相关问题