你正在使用哪个版本的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上,这对于可读性真的很糟糕
4条答案
按热度按时间oiopk7p51#
我们目前不会通过乘法传播已知范围。在prove.go中实现应该不难。
uz75evzq2#
https://golang.org/cl/248701提到了这个问题:
cmd/compile: add support for Lsh64*64 and Mul64 to IsSliceInBounds
xesrikrc3#
另一个密码学实现者加入讨论!很高兴看到这个改进,理想情况下,它也可以用于除乘法之外的其他操作。它将特别允许我在不展开循环的情况下提高代码的可读性,而不会出现明显的(在一个任务上约30%的性能损失,参见gtank/blake2#2(评论))性能损失。
我努力保持这个库是纯Go语言编写的,这使得在生产部署中,我更有可能需要引入汇编来实现算法本身所期望的吞吐量(cc @FiloSottile)。
3b6akqbq4#
https://go.dev/cl/599256提到了这个问题:
cmd/compile: propagate constant ranges through multiplies and shifts