为了账号安全,请及时绑定邮箱和手机立即绑定

golang切片分配性能

golang切片分配性能

Go
守着一只汪 2021-12-27 15:13:14
在检查 GO 中内存分配的性能时,我偶然发现了一件有趣的事情。package mainimport (      "fmt"      "time"    )func main(){   const alloc int = 65536   now := time.Now()   loop := 50000   for i := 0; i<loop;i++{      sl := make([]byte, alloc)      i += len(sl) * 0   }   elpased := time.Since(now)   fmt.Printf("took %s to allocate %d bytes %d times", elpased, alloc, loop) }我在 Core-i7 2600 上运行它,Go 版本为 1.6 64 位(在 32 位上也有相同的结果)和 16GB 的 RAM(在 WINDOWS 10 上),所以当 alloc 是 65536(正好是 64K)时,它会运行 30 秒(!!!!! )。当 alloc 为 65535 时,大约需要 200 毫秒。有人可以向我解释一下吗?我在家里用我的核心 i7-920 @ 3.8GHZ 尝试了相同的代码,但它没有显示相同的结果(两者都花了大约 200 毫秒)。任何人都知道发生了什么?
查看完整描述

3 回答

?
长风秋雁

TA贡献1757条经验 获得超7个赞

设置 GOGC=off 提高了性能(降低到小于 100 毫秒)。为什么?因为逃逸分析。当您使用go build -gcflags -m编译器构建时,会打印任何逃逸到堆的分配。这实际上取决于您的机器和 GO 编译器版本,但是当编译器决定分配应该移到堆时,这意味着两件事:1. 分配将花费更长的时间(因为堆栈上的“分配”只是 1 个 cpu 指令)2. GC 稍后将不得不清理该内存 - 为我的机器花费更多的 CPU 时间,65536 字节的分配转义到堆,而 65535 则不会。这就是为什么 1 个字节将整个过程从 200 毫秒更改为 30 秒的原因。惊人..


查看完整回答
反对 回复 2021-12-27
?
慕森卡

TA贡献1806条经验 获得超8个赞

注意/2021 年更新:正如Tapir Liui在Go101中用这条推文指出的那样:


从 Go 1.17 开始,如果编译器证明它们仅在当前 goroutine 中使用,并且Go 运行时将在堆栈上分配slice 的元素:xN <= 64KB


var x = make([]byte, N)

如果编译器证明它仅用于当前 goroutine并且 Go 运行时将在堆栈上分配数组 :yN <= 10MB


var y [N]byte

那么如何分配的(的元素)片,其尺寸大于64KB上堆叠更大但不大于10MB(和切片仅在一个够程中使用)?


只需使用以下方式:


var y [N]byte

var x = y[:]

考虑到堆栈分配比堆分配快,这将对您的测试产生直接影响,因为alloc等于 65536 或更多。


貘补充说:


事实上,我们可以在堆栈上分配具有任意总和元素大小的切片。


const N = 500 * 1024 * 1024 // 500M

var v byte = 123


func createSlice() byte {

 var s = []byte{N: 0}

 for i := range s { s[i] = v }

 return s[v]

}

将 500 更改为 512 会使程序崩溃。


查看完整回答
反对 回复 2021-12-27
?
叮当猫咪

TA贡献1776条经验 获得超12个赞

原因很简单。

const alloc int = 65535

0x0000 00000 (example.go:8)     TEXT    "".main(SB), ABIInternal, $65784-0

const alloc int = 65536

0x0000 00000 (example.go:8)     TEXT    "".main(SB), ABIInternal, $248-0

不同之处在于切片的创建位置。


查看完整回答
反对 回复 2021-12-27
  • 3 回答
  • 0 关注
  • 220 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信