go `cmd/compile:自动分配小尺寸的动态大小非逃逸切片`

cwxwcias  于 7个月前  发布在  Go
关注(0)|答案(7)|浏览(61)

这个提交:
95a11c7
展示了一个通过将一个小的非转义切片移动到栈上触发的实际性能提升。我的理解是,Go编译器总是在堆上分配切片,因为长度在编译时未知。
是否有必要尝试对许多/所有非转义切片进行类似的代码转换?这样做有什么缺点?关于如何识别哪些切片可以从这种转换中受益以及哪些可能会产生开销,有什么建议吗?

ajsxfq5m

ajsxfq5m1#

我认为如果我们可以的话,从堆栈开始几乎总是一个赢的策略。
这个例子很棘手,可能非常常见:

var b []byte
for ... {
    b = append(b, ...)
}

我们如何预先为 b 分配一些空间?我们希望这样做:

var bStore [32]byte  // on stack
b := bStore[:0]

但如果 for 循环运行0次,那么这是不正确的。结果必须是 nil (并具有0容量)。

cx6n0qe3

cx6n0qe32#

我想知道,如果始终应用这种转换,即使它从未影响性能,是否会使二进制文件明显变大。
这是否会为所有分配的小非逃逸切片完成?还是仅针对那些已知在编译时容量较小的切片?

klr1opcd

klr1opcd3#

我们已经在编译时为已知容量的小非逃逸切片分配了栈空间。这个问题是关于在编译时未知大小的情况。

q9yhzks0

q9yhzks05#

它确实很相似。#20533 在你执行 a := make([]byte, n) (alloca风格的分配,或者在堆上显式释放)时,会沿着真正分配n字节的道路走得更远。这个函数是关于分配一个固定大小的缓冲区,并且只在 n 足够小的时候使用它。

6tqwzwtp

6tqwzwtp6#

a := make([]byte, n) 转换为:

var a []byte
if n < 64 {
    a = make([]byte, n, 64)   // stack allocation
} else {
    a = make([]byte, n)       // heap allocation
}

肯定会对代码大小产生一定影响。在某些情况下,也许证明能够移除两个分支中的一个,但我不抱太大希望。我想知道从性能Angular 来看,这样做是否仍然值得。
我们还应该探索对不同类型切片执行此操作(同时保持总堆分配在一定限制内)。

kmbjn2e3

kmbjn2e37#

Transforming a := make([]byte, n) into ...
Recent example where such transformation was done by hand for performance: 17d5cef ( CL 230657 ).
/cc @martisch

相关问题