Go语言数据存取方式性能消耗对比实验

Go性能实验 by 达达 at 2013-10-30

在开发过程中,我们总是在设计不同的数据结构和不同的算法对数据进行组织和存取,有些场合我们可以用硬编码或者代码生成器来生成一组函数按条件返回数据,另外一些场合我们可以把数据存储在slice当中,需要查找时就遍历查找,又或者有的场合我们可以用map存储数据,用key来索引数据。这几种数据存取方式在不同数据量的情况下,性能消耗是怎么样的呢?下面做个对比实验。

我们知道go的map数据量不管大小都能保证固定的读取时间,而slice和硬编码肯定无法应对大数据量,所以这里不做大数据量的实验,只做小数据量的实验。

实验代码(github):

package labs06

import "testing"

type BigStruct struct {
    C01 int
    C02 int
    C03 int
    C04 int
    C05 int
    C06 int
    C07 int
    C08 int
    C09 int
    C10 int
    C11 int
    C12 int
    C13 int
    C14 int
    C15 int
    C16 int
    C17 int
    C18 int
    C19 int
    C20 int
    C21 int
    C22 int
    C23 int
    C24 int
    C25 int
    C26 int
    C27 int
    C28 int
    C29 int
    C30 int
}

func Loop1(a []BigStruct) int {
    for i := 0; i < len(a); i++ {
        if a[i].C30 == 3 {
            return i
        }
    }

    return -1
}

func Loop2(a []BigStruct) int {
    for i := len(a) - 1; i >= 0; i-- {
        if a[i].C30 == 1 {
            return i
        }
    }

    return -1
}

func Loop3(a map[int]BigStruct) int {
    return a[2].C30
}

func Loop4(a []*BigStruct) int {
    for i, x := range a {
        if x.C30 == 3 {
            return i
        }
    }

    return -1
}

func Loop5(a []BigStruct) int {
    switch {
    case a[0].C01 == 3:
        return 0
    case a[1].C01 == 3:
        return 1
    case a[2].C01 == 3:
        return 2
    }

    return -1
}

func Benchmark_Loop1(b *testing.B) {
    var a = make([]BigStruct, 3)

    a[0].C30 = 1
    a[1].C30 = 2
    a[2].C30 = 3

    for i := 0; i < b.N; i++ {
        Loop1(a)
    }
}

func Benchmark_Loop2(b *testing.B) {
    var a = make([]BigStruct, 3)

    a[0].C30 = 1
    a[1].C30 = 2
    a[2].C30 = 3

    for i := 0; i < b.N; i++ {
        Loop2(a)
    }
}

func Benchmark_Loop3(b *testing.B) {
    var a = make(map[int]BigStruct, 3)

    a[0] = BigStruct{C30: 1}
    a[1] = BigStruct{C30: 2}
    a[2] = BigStruct{C30: 3}

    for i := 0; i < b.N; i++ {
        Loop3(a)
    }
}

func Benchmark_Loop4(b *testing.B) {
    var a = make([]*BigStruct, 3)

    a[0] = &BigStruct{C30: 1}
    a[1] = &BigStruct{C30: 2}
    a[2] = &BigStruct{C30: 3}

    for i := 0; i < b.N; i++ {
        Loop4(a)
    }
}

func Benchmark_Loop5(b *testing.B) {
    var a = make([]BigStruct, 3)

    a[0].C30 = 1
    a[1].C30 = 2
    a[2].C30 = 3

    for i := 0; i < b.N; i++ {
        Loop5(a)
    }
}

试验结果:

dada-imac:labs dada$ go test -test.bench="." labs06
testing: warning: no tests to run
PASS
Benchmark_Loop1    500000000             5.73 ns/op
Benchmark_Loop2    500000000             5.72 ns/op
Benchmark_Loop3    50000000              68.0 ns/op
Benchmark_Loop4    500000000             4.92 ns/op
Benchmark_Loop5    500000000             4.40 ns/op
ok      labs06  15.970s    

结论:硬编码 < 指针slice的range循环 < for循环,但是量级是一样的,看情况用。但是map差了一个量级,小数据量尽量少用。