关于go:解释预分配切片的基准

Interpretting benchmarks of preallocating a slice

我一直在试图理解使用make进行切片预分配,以及为什么它是一个好主意。我注意到预分配一个切片和附加到它和只初始化0长度/容量然后附加到它之间有很大的性能差异。我写了一套非常简单的基准:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import"testing"

func BenchmarkNoPreallocate(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // Don't preallocate our initial slice
        init := []int64{}
        init = append(init, 5)
    }
}

func BenchmarkPreallocate(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // Preallocate our initial slice
        init := make([]int64, 0, 1)
        init = append(init, 5)
    }
}

对结果有点困惑:

1
2
3
4
5
$ go test -bench=. -benchmem
goos: linux
goarch: amd64
BenchmarkNoPreallocate-4    30000000            41.8 ns/op         8 B/op          1 allocs/op
BenchmarkPreallocate-4      2000000000           0.29 ns/op        0 B/op          0 allocs/op

我有几个问题:

  • 为什么在预分配基准案例中没有分配(它显示0个分配/op)?当然,我们正在进行预分配,但分配必须在某个时刻发生。
  • 我想在第一个问题被回答之后,这可能会变得更清楚,但是预分配的情况怎么会这么快呢?我是否误解了这个基准?
  • 小精灵

    如果有什么不清楚的地方请告诉我。谢谢您!


    Go有一个优化编译器。常量在编译时计算。变量在运行时计算。常量值可用于优化编译器生成的代码。例如,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    package main

    import"testing"

    func BenchmarkNoPreallocate(b *testing.B) {
        for i := 0; i < b.N; i++ {
            // Don't preallocate our initial slice
            init := []int64{}
            init = append(init, 5)
        }
    }

    func BenchmarkPreallocateConst(b *testing.B) {
        const (
            l = 0
            c = 1
        )
        for i := 0; i < b.N; i++ {
            // Preallocate our initial slice
            init := make([]int64, l, c)
            init = append(init, 5)
        }
    }

    func BenchmarkPreallocateVar(b *testing.B) {
        var (
            l = 0
            c = 1
        )
        for i := 0; i < b.N; i++ {
            // Preallocate our initial slice
            init := make([]int64, l, c)
            init = append(init, 5)
        }
    }

    输出:

    1
    2
    3
    4
    $ go test alloc_test.go -bench=. -benchmem
    BenchmarkNoPreallocate-4         50000000    39.3 ns/op     8 B/op    1 allocs/op
    BenchmarkPreallocateConst-4    2000000000     0.36 ns/op    0 B/op    0 allocs/op
    BenchmarkPreallocateVar-4        50000000    28.2 ns/op     8 B/op    1 allocs/op

    另一组有趣的基准:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    package main

    import"testing"

    func BenchmarkNoPreallocate(b *testing.B) {
        const (
            l = 0
            c = 8 * 1024
        )
        for i := 0; i < b.N; i++ {
            // Don't preallocate our initial slice
            init := []int64{}
            for j := 0; j < c; j++ {
                init = append(init, 42)
            }
        }
    }

    func BenchmarkPreallocateConst(b *testing.B) {
        const (
            l = 0
            c = 8 * 1024
        )
        for i := 0; i < b.N; i++ {
            // Preallocate our initial slice
            init := make([]int64, l, c)
            for j := 0; j < cap(init); j++ {
                init = append(init, 42)
            }
        }
    }

    func BenchmarkPreallocateVar(b *testing.B) {
        var (
            l = 0
            c = 8 * 1024
        )
        for i := 0; i < b.N; i++ {
            // Preallocate our initial slice
            init := make([]int64, l, c)
            for j := 0; j < cap(init); j++ {
                init = append(init, 42)
            }
        }
    }

    输出:

    1
    2
    3
    4
    $ go test peter_test.go -bench=. -benchmem
    BenchmarkNoPreallocate-4       20000   75656 ns/op   287992 B/op   19 allocs/op
    BenchmarkPreallocateConst-4   100000   22386 ns/op    65536 B/op    1 allocs/op
    BenchmarkPreallocateVar-4     100000   22112 ns/op    65536 B/op    1 allocs/op