go1.16
以下函数通常会引发两次分配,每次调用一次。然而,编译器可能提前知道额外数据的长度(len(f) + 4),因此最多只能分配一次。
func appendFoo(x []byte, f string) []byte {
x = append(x, "aggf"...)
x = append(x, f...)
return x
}
解决这个问题的方法很尴尬,因为要做到这一点需要复制 append
容量增长算法,以便切片可以通过乘法而不是常数因子增长。当泛型到来时,我希望在标准库中看到这样的函数(这对 #14325 等其他函数很有用),但上述优化仍然有用,可以避免使用它。
// Grow returns ts with its capacity grown enough to accommodate n more items.
func Grow[T any](ts []T, n int) []T
相关问题:
- cmd/compile: recognize append loop #14325 - 这种优化方法,但带有循环
- cmd/compile: combine append calls #25828 - 当几个追加调用可以合并为一个时(这里不是这种情况)
- cmd/compile: better append of unmodified slices #37694 - 特别与数组相关
4条答案
按热度按时间mfpqipee1#
I may be mistaken, but doesnt the following allow growing the capacity accordingly and is optimized to not allocate the make?
s = append(s, make([]T, n)...)
f2uvfpb92#
我认为编译器永远无法优化这个,因为每次append操作都会在函数外部产生可观察到的效果。请比较以下内容:
https://play.golang.org/p/WvBXW8u1SWk
https://play.golang.org/p/nfqTnI8SrnV
https://play.golang.org/p/N2dtswu2dLC
laik7k3q3#
仍然是可能的,但很棘手。@rogpeppe担心的是当两个append都导致增长时。在这种情况下,中间的增长是不可见的,可以跳过。但是是的,你确实需要将所有可能的append写入原始切片。
尽管如此,@pierrec的解决方法应该能处理这个问题。我不确定编译器中的专用代码是否值得这样做。
fnx2tebb4#
一个非常相似的案例是 #25828 。我们可以将缺失的信息从一个问题移动到另一个问题(两个中的任何一个都可以),并关闭另一个问题。有关一些见解,请参阅 #25828 (评论)。