gpt4 book ai didi

go make slice 比 []{1,1,1,1} 慢一点

转载 作者:IT王子 更新时间:2023-10-29 02:10:50 25 4
gpt4 key购买 nike

我正在开发一个分配大量长度为 4,3,2 的 []int 的程序
发现使用 a:=[]{1,1,1}a:=make([]int,3) a[0] = 1 a[ 1]=1 a[2]= 1

我的问题:为什么 a:=[]{1,1,1}a:=make([]int,3) a[0] = 1 a 快[1]=1 a[2]= 1?

func BenchmarkMake(b *testing.B) {
var array []int
for i := 0; i < b.N; i++ {
array = make([]int, 4)
array[0] = 1
array[1] = 1
array[2] = 1
array[3] = 1
}
}

func BenchmarkDirect(b *testing.B) {
var array []int
for i := 0; i < b.N; i++ {
array = []int{1, 1, 1, 1}
}

array[0] = 1
}

BenchmarkMake-4 50000000 34.3 纳秒/运算
BenchmarkDirect-4 50000000 33.8 纳秒/操作

最佳答案

让我们看看以下代码的基准输出

package main

import "testing"

func BenchmarkMake(b *testing.B) {
var array []int
for i := 0; i < b.N; i++ {
array = make([]int, 4)
array[0] = 1
array[1] = 1
array[2] = 1
array[3] = 1
}
}

func BenchmarkDirect(b *testing.B) {
var array []int
for i := 0; i < b.N; i++ {
array = []int{1, 1, 1, 1}
}
array[0] = 1
}

func BenchmarkArray(b *testing.B) {
var array [4]int
for i := 0; i < b.N; i++ {
array = [4]int{1, 1, 1, 1}
}
array[0] = 1
}

通常输出是这样的

$ go test -bench . -benchmem -o alloc_test -cpuprofile cpu.prof
goos: linux
goarch: amd64
pkg: test
BenchmarkMake-8 30000000 61.3 ns/op 32 B/op 1 allocs/op
BenchmarkDirect-8 20000000 60.2 ns/op 32 B/op 1 allocs/op
BenchmarkArray-8 1000000000 2.56 ns/op 0 B/op 0 allocs/op
PASS
ok test 6.003s

差别很小,在某些情况下可能相反。

让我们看看分析数据

$go tool pprof -list 'Benchmark.*' cpu.prof 

ROUTINE ======================== test.BenchmarkMake in /home/grzesiek/go/src/test/alloc_test.go
260ms 1.59s (flat, cum) 24.84% of Total
. . 5:func BenchmarkMake(b *testing.B) {
. . 6: var array []int
40ms 40ms 7: for i := 0; i < b.N; i++ {
50ms 1.38s 8: array = make([]int, 4)
. . 9: array[0] = 1
130ms 130ms 10: array[1] = 1
20ms 20ms 11: array[2] = 1
20ms 20ms 12: array[3] = 1
. . 13: }
. . 14:}
ROUTINE ======================== test.BenchmarkDirect in /home/grzesiek/go/src/test/alloc_test.go
90ms 1.66s (flat, cum) 25.94% of Total
. . 16:func BenchmarkDirect(b *testing.B) {
. . 17: var array []int
10ms 10ms 18: for i := 0; i < b.N; i++ {
80ms 1.65s 19: array = []int{1, 1, 1, 1}
. . 20: }
. . 21: array[0] = 1
. . 22:}
ROUTINE ======================== test.BenchmarkArray in /home/grzesiek/go/src/test/alloc_test.go
2.86s 2.86s (flat, cum) 44.69% of Total
. . 24:func BenchmarkArray(b *testing.B) {
. . 25: var array [4]int
500ms 500ms 26: for i := 0; i < b.N; i++ {
2.36s 2.36s 27: array = [4]int{1, 1, 1, 1}
. . 28: }
. . 29: array[0] = 1
. . 30:}

我们可以看到分配需要一些时间。

了解为什么我们需要查看汇编代码。

$go tool pprof -disasm 'BenchmarkMake' cpu.prof

. . 4eda93: MOVQ AX, 0(SP) ;alloc_test.go:8
30ms 30ms 4eda97: MOVQ $0x4, 0x8(SP) ;test.BenchmarkMake alloc_test.go:8
. . 4edaa0: MOVQ $0x4, 0x10(SP) ;alloc_test.go:8
10ms 1.34s 4edaa9: CALL runtime.makeslice(SB) ;test.BenchmarkMake alloc_test.go:8
. . 4edaae: MOVQ 0x18(SP), AX ;alloc_test.go:8
10ms 10ms 4edab3: MOVQ 0x20(SP), CX ;test.BenchmarkMake alloc_test.go:8
. . 4edab8: TESTQ CX, CX ;alloc_test.go:9
. . 4edabb: JBE 0x4edb0b
. . 4edabd: MOVQ $0x1, 0(AX)
130ms 130ms 4edac4: CMPQ $0x1, CX ;test.BenchmarkMake alloc_test.go:10
. . 4edac8: JBE 0x4edb04 ;alloc_test.go:10
. . 4edaca: MOVQ $0x1, 0x8(AX)
20ms 20ms 4edad2: CMPQ $0x2, CX ;test.BenchmarkMake alloc_test.go:11
. . 4edad6: JBE 0x4edafd ;alloc_test.go:11
. . 4edad8: MOVQ $0x1, 0x10(AX)
. . 4edae0: CMPQ $0x3, CX ;alloc_test.go:12
. . 4edae4: JA 0x4eda65

我们可以看到,CMPQ 命令将常量与 CX 寄存器进行比较,从而占用了时间。 CX 寄存器是调用 make 后从堆栈复制的值。我们可以推断它必须是 slice 的大小,而 AX 持有对底层数组的引用。您还可以看到优化了第一个绑定(bind)检查。

结论

  1. 分配需要相同的时间,但由于 slice 大小检查(正如 Terry Pang 所注意到的),分配会产生额外费用。
  2. 使用数组而不是 slice 更便宜,因为它节省了分配。

为什么使用数组便宜得多?

在 Go 中,数组基本上是一 block 固定大小的内存。 [1]intint 基本相同。您可以在 Go Slices: usage and internals 中找到更多信息文章。

关于go make slice 比 []{1,1,1,1} 慢一点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47569069/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com