go `cmd/compile:避免连续追加操作时的不必要的分配`

1rhkuytd  于 6个月前  发布在  Go
关注(0)|答案(4)|浏览(45)

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

相关问题:

mfpqipee

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)...)

f2uvfpb9

f2uvfpb92#

我认为编译器永远无法优化这个,因为每次append操作都会在函数外部产生可观察到的效果。请比较以下内容:
https://play.golang.org/p/WvBXW8u1SWk
https://play.golang.org/p/nfqTnI8SrnV
https://play.golang.org/p/N2dtswu2dLC

laik7k3q

laik7k3q3#

仍然是可能的,但很棘手。@rogpeppe担心的是当两个append都导致增长时。在这种情况下,中间的增长是不可见的,可以跳过。但是是的,你确实需要将所有可能的append写入原始切片。
尽管如此,@pierrec的解决方法应该能处理这个问题。我不确定编译器中的专用代码是否值得这样做。

fnx2tebb

fnx2tebb4#

一个非常相似的案例是 #25828 。我们可以将缺失的信息从一个问题移动到另一个问题(两个中的任何一个都可以),并关闭另一个问题。有关一些见解,请参阅 #25828 (评论)。

相关问题