繁体   English   中英

golang切片分配性能

[英]golang slice allocation performance

在检查 GO 中内存分配的性能时,我偶然发现了一件有趣的事情。

package main

import (
      "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 毫秒)。 任何人都知道发生了什么?

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

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

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

 var x = make([]byte, N)

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

 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 会使程序崩溃。

原因很简单。

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

区别在于创建切片的位置。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM