Bry*_*yce 11 performance go slice
为了使切片附加操作更快,我们需要分配足够的容量.有两种方法可以追加切片,这是代码:
func BenchmarkSliceAppend(b *testing.B) {
a := make([]int, 0, b.N)
for i := 0; i < b.N; i++ {
a = append(a, i)
}
}
func BenchmarkSliceSet(b *testing.B) {
a := make([]int, b.N)
for i := 0; i < b.N; i++ {
a[i] = i
}
}
Run Code Online (Sandbox Code Playgroud)
结果是:
BenchmarkSliceAppend-4 200000000 7.87 ns/op 8 B/op 0 allocs/op
BenchmarkSliceSet-4 300000000 5.76 ns/op 8 B/op
a[i] = i比a = append(a, i)我快,我想知道为什么?
icz*_*cza 12
a[i] = i只需将值赋值i给a[i].这不是追加,只是一个简单的任务.
现在附加:
a = append(a, i)
Run Code Online (Sandbox Code Playgroud)
理论上会发生以下情况:
这会调用内置append()函数.为此,它首先必须复制a切片(切片标头,支持数组不是标头的一部分),并且必须为包含该值的可变参数创建临时切片i.
然后,a如果它有足够的容量(在你的情况下),它必须重新组合a = a[:len(a)+1]- 这涉及将新切片分配到a内部append().
(如果a没有足够的容量来执行"就地"附加,则必须分配一个新数组,复制切片的内容,然后执行assign/append - 但这不是这种情况.)
然后分配i给a[len(a)-1].
然后从中返回新切片append(),并将此新切片分配给局部变量a.
与简单的任务相比,这里发生了很多事情.即使这些步骤中的许多步骤被优化和/或内联,作为分配i给切片的元素的最小添加,也必须在循环的每个循环中更新切片类型的局部变量a(其是切片报头).
推荐阅读:Go Blog:数组,切片(和字符串):'追加'的机制
自从发布此问题以来,似乎已经引入了对Go编译器或运行时的一些改进,因此,现在(Go 1.10.1)append与按索引直接分配之间没有显着差异。
另外,由于OOM恐慌,我不得不稍微更改您的基准。
package main
import "testing"
var result []int
const size = 32
const iterations = 100 * 1000 * 1000
func doAssign() {
data := make([]int, size)
for i := 0; i < size; i++ {
data[i] = i
}
result = data
}
func doAppend() {
data := make([]int, 0, size)
for i := 0; i < size; i++ {
data = append(data, i)
}
result = data
}
func BenchmarkAssign(b *testing.B) {
b.N = iterations
for i := 0; i < b.N; i++ {
doAssign()
}
}
func BenchmarkAppend(b *testing.B) {
b.N = iterations
for i := 0; i < b.N; i++ {
doAppend()
}
}
Run Code Online (Sandbox Code Playgroud)
结果:
? bench_slice_assign go test -bench=Bench .
goos: linux
goarch: amd64
BenchmarkAssign-4 100000000 80.9 ns/op
BenchmarkAppend-4 100000000 81.9 ns/op
PASS
ok _/home/isaev/troubles/bench_slice_assign 16.288s
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10335 次 |
| 最近记录: |