猿问

golang切片分配性能

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


守着一只汪
浏览 221回答 3
3回答

长风秋雁

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

慕森卡

注意/2021 年更新:正如Tapir Liui在Go101中用这条推文指出的那样:从 Go 1.17 开始,如果编译器证明它们仅在当前 goroutine 中使用,并且Go 运行时将在堆栈上分配slice 的元素:xN <= 64KBvar x = make([]byte, N)如果编译器证明它仅用于当前 goroutine并且 Go 运行时将在堆栈上分配数组 :yN <= 10MBvar y [N]byte那么如何分配的(的元素)片,其尺寸大于64KB上堆叠更大但不大于10MB(和切片仅在一个够程中使用)?只需使用以下方式:var y [N]bytevar x = y[:]考虑到堆栈分配比堆分配快,这将对您的测试产生直接影响,因为alloc等于 65536 或更多。貘补充说:事实上,我们可以在堆栈上分配具有任意总和元素大小的切片。const N = 500 * 1024 * 1024 // 500Mvar v byte = 123func createSlice() byte {&nbsp;var s = []byte{N: 0}&nbsp;for i := range s { s[i] = v }&nbsp;return s[v]}将 500 更改为 512 会使程序崩溃。

叮当猫咪

原因很简单。const alloc int = 655350x0000 00000 (example.go:8) &nbsp; &nbsp; TEXT &nbsp; &nbsp;"".main(SB), ABIInternal, $65784-0const alloc int = 655360x0000 00000 (example.go:8) &nbsp; &nbsp; TEXT &nbsp; &nbsp;"".main(SB), ABIInternal, $248-0不同之处在于切片的创建位置。
随时随地看视频慕课网APP

相关分类

Go
我要回答