解释预分配切片的基准

时间:2017-11-09 02:05:32

标签: go slice

我一直在尝试用make理解切片预分配,为什么这是一个好主意。我注意到在预分配切片和附加切片与仅使用0长度/容量初始化然后附加到切片之间存在很大的性能差异。我写了一组非常简单的基准:

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)
    }
}

对结果感到有些困惑:

$ 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个allocs / op)?当然我们正在预先分配,但分配已经发生在某个时刻。
  • 我想在第一个问题得到解答后,这可能会变得更加清晰,但预分配案例如何更快?我是否错配了这个基准?

如果有任何不清楚的地方,请告诉我。谢谢!

1 个答案:

答案 0 :(得分:3)

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

[Key]
public int SubTech { get; set; }
[Display(Name ="SUBJECT")]
public int SubjectID { get; set; }
public virtual Subject Subjects { get; set; }
[Display(Name = "TEACHER")]
public int UserID { get; set; }
public ICollection< User> Users { get; set; }
[Display(Name = "LEVEL")]
public int LevelID { get; set; }
public ICollection< Level> Levels { get; set; }

输出:

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)
    }
}

另一组有趣的基准:

$ 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

输出:

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)
        }
    }
}