为什么追加预分配切片比索引更快?

Ale*_*ler 3 append go

我编写了以下Go基准测试(Go1.9.2,Linux amd64)来测试索引与追加:

func BenchmarkIndex(b *testing.B) {
    for i := 0; i < b.N; i++ {
        existing := make([]int64, 1000, 1000)
        init := make([]int64, 1000) // len 1000, cap 1000
        for index, element := range existing {
            init[index] = element
        }
    }
}

func BenchmarkAppend(b *testing.B) {
    for i := 0; i < b.N; i++ {
        existing := make([]int64, 1000, 1000)
        init := make([]int64, 0, 1000) // len 0, capacity 1000
        for _, element := range existing {
            init = append(init, element)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并得到以下结果:

goos: linux
goarch: amd64
BenchmarkIndex-4         1000000          2183 ns/op
BenchmarkAppend-4        1000000          1933 ns/op
PASS
Run Code Online (Sandbox Code Playgroud)

我有点困惑为什么append看起来比一般表现更好而不是索引切片.是否与省略index范围内有任何关系?如果没有,那么在引擎盖下做什么是为了使它更快而不仅仅是索引?

作为参考,这是append的来源.谢谢!

pet*_*rSO 8

在电脑上:

$ go test same_test.go -run=! -bench=. -benchmem -count=3
goos: linux
goarch: amd64
BenchmarkIndex-4     1000000    1290 ns/op    0 B/op    0 allocs/op
BenchmarkIndex-4     1000000    1290 ns/op    0 B/op    0 allocs/op
BenchmarkIndex-4     1000000    1288 ns/op    0 B/op    0 allocs/op
BenchmarkAppend-4    1000000    1299 ns/op    0 B/op    0 allocs/op
BenchmarkAppend-4    1000000    1302 ns/op    0 B/op    0 allocs/op
BenchmarkAppend-4    1000000    1301 ns/op    0 B/op    0 allocs/op
PASS
ok      command-line-arguments  7.868s
$ go version
go version devel +38c725b148 Sun Nov 5 17:30:11 2017 +0000 linux/amd64
$ 
Run Code Online (Sandbox Code Playgroud)

BenchmarkAppend和之间的区别BenchmarkIndex是:

((1299+1302+1301) - (1290+1290+1288)) * 100 / (1290+1290+1288) =  +0.879%
Run Code Online (Sandbox Code Playgroud)

他们是一样的.

在电脑二:

$ go test same_test.go -run=! -bench=. -benchmem -count=3
goos: linux
goarch: amd64
BenchmarkIndex-8     2000000    706 ns/op    0 B/op    0 allocs/op
BenchmarkIndex-8     2000000    713 ns/op    0 B/op    0 allocs/op
BenchmarkIndex-8     2000000    710 ns/op    0 B/op    0 allocs/op
BenchmarkAppend-8    2000000    711 ns/op    0 B/op    0 allocs/op
BenchmarkAppend-8    2000000    718 ns/op    0 B/op    0 allocs/op
BenchmarkAppend-8    2000000    718 ns/op    0 B/op    0 allocs/op
PASS
ok      command-line-arguments  12.945s
$ go version
go version devel +38c725b148 Sun Nov 5 17:30:11 2017 +0000 linux/amd64
$ 
Run Code Online (Sandbox Code Playgroud)

BenchmarkAppend和之间的区别BenchmarkIndex是:

((711+718+718) - (706+713+710)) * 100 / (706+713+710) =  +0.845%
Run Code Online (Sandbox Code Playgroud)

他们是一样的.


让我们一次测量一件事,索引与附加在预分配切片上,这避免了内存管理等混淆问题.

package main

import "testing"

func BenchmarkIndexing(b *testing.B) {
    existing := make([]int64, 1000, 1000)
    init := make([]int64, 1000) // len 1000, cap 1000
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        init = init[:cap(init)]
        for index, element := range existing {
            init[index] = element
        }
    }
}

func BenchmarkAppending(b *testing.B) {
    existing := make([]int64, 1000, 1000)
    init := make([]int64, 0, 1000) // len 0, capacity 1000
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        init = init[:0]
        for _, element := range existing {
            init = append(init, element)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

$ go test same_test.go -run=! -bench=ing -benchmem -count=3
goos: linux
goarch: amd64
BenchmarkIndexing-4     2000000    736 ns/op    0 B/op    0 allocs/op
BenchmarkIndexing-4     2000000    735 ns/op    0 B/op    0 allocs/op
BenchmarkIndexing-4     2000000    735 ns/op    0 B/op    0 allocs/op
BenchmarkAppending-4    2000000    733 ns/op    0 B/op    0 allocs/op
BenchmarkAppending-4    2000000    733 ns/op    0 B/op    0 allocs/op
BenchmarkAppending-4    2000000    733 ns/op    0 B/op    0 allocs/op
PASS
ok      command-line-arguments  13.303s
$ go version
go version devel +38c725b148 Sun Nov 5 17:30:11 2017 +0000 linux/amd64
$
Run Code Online (Sandbox Code Playgroud)

BenchmarkAppend和之间的区别BenchmarkIndex是:

((733+733+733) - (736+735+735)) * 100 / (736+735+735) =  ?0.317%
Run Code Online (Sandbox Code Playgroud)

他们是一样的.