在golang中测试函数

时间:2017-03-26 00:47:54

标签: testing go

所以我决定在golang为我的功能编写测试。该函数本身如下:

func Insert(slice []int, element int, index int) []int {
    n := len(slice)
    slice = slice[:(n + 1)]
    for i := index; i < n; i++ {
        slice[i+1] = slice[i]
    }
    slice[index] = element
    return slice
} 

因此它需要一个切片,将其长度延长1并在给定索引处插入一个元素。在我打电话之前,我必须做两件事:

size := somevalue
array := make([]int, size, size+1)

然后f.e array = Insert(array, 1, len(array)/2。现在我写了insert_test.go

package sdizo

import "testing"

func benchmarkInsert(size int, b *testing.B) {  
    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        array := make([]int, size, size+1)
        array = Insert(array, 1, len(array)/2)
    }
}
func BenchmarkInsert10k(b *testing.B) { benchmarkInsert(10000, b) }
func BenchmarkInsert20k(b *testing.B) { benchmarkInsert(20000, b) }
func BenchmarkInsert30k(b *testing.B) { benchmarkInsert(30000, b) }
func BenchmarkInsert40k(b *testing.B) { benchmarkInsert(40000, b) }
func BenchmarkInsert50k(b *testing.B) { benchmarkInsert(50000, b) }

问题是,测试循环中有2个操作。我无法将make()移动到循环上方,因为每次尝试向其插入内容时都必须创建一个新数组(不要让容量混乱)。它有效,它给了我输出,但我很好奇,如果make()没有弄乱时间测量,我想问你是否我能以某种方式衡量{{1}的时间}

P.S是否有方便的方法将基准测试结果写入文件?

2 个答案:

答案 0 :(得分:1)

编辑:你寻求的真正答案。 (最后)

当您进行基准测试时,实际上会对您想要的基准进行基准测试你的用例不是make,然后每次都插入,所以只需make一次,然后在循环中测试insert。关键是只测试阻塞点。

您想要做的只是测试Insert。你可以通过简单地每次调整数组大小来实现这一点,其成本基本上没有(至少与makeInsert相比)。

func benchmarkInsert(size int, b *testing.B) { 
    array := make([]int, size, size+1) 
    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        array = Insert(array, 1, len(array)/2)
        array = array[0:size]
    }
}

要解释为什么它是最小的,你必须意识到,在内心深处,切片本质上是一个看起来像这样的结构:

type Slice struct {
    contents []interface{}
    length   int
    capacity int
}

使用array = array[0:size]执行调整大小的操作类似于设置结构length=size。见https://blog.golang.org/go-slices-usage-and-internals

至于将结果写入文件,这取决于您的操作系统。在任何Unix系统上,只需在末尾运行带有> file.txt的基准命令,就会将结果传递给file.txt。对Windows不确定。

编辑:添加更多测试表明这是线性扩展。

BenchmarkInsert10k-8          200000          7889 ns/op
BenchmarkInsert20k-8          100000         16131 ns/op
BenchmarkInsert30k-8          100000         24184 ns/op
BenchmarkInsert40k-8           50000         31767 ns/op
BenchmarkInsert50k-8           50000         39721 ns/op
BenchmarkInsert100k-8          20000         79711 ns/op
BenchmarkInsert200k-8          10000        159411 ns/op
BenchmarkInsert300k-8           5000        237188 ns/op
BenchmarkInsert400k-8           5000        316270 ns/op
BenchmarkInsert500k-8           3000        399146 ns/op
BenchmarkInsert1000k-8          2000        793845 ns/op

使用此代码:https://play.golang.org/p/6fWHwpzUJE

答案 1 :(得分:1)

例如(从Go 1.7开始),使用您的Insert算法,

package sdizo

import (
    "strconv"
    "testing"
)

func Insert(slice []int, element int, index int) []int {
    n := len(slice)
    slice = slice[:(n + 1)]
    for i := index; i < n; i++ {
        slice[i+1] = slice[i]
    }
    slice[index] = element
    return slice
}

func BenchmarkInsert(b *testing.B) {
    for size := 10000; size <= 50000; size += 10000 {

        b.Run(strconv.Itoa(size/1000)+"k",

            func(b *testing.B) {
                a := make([]int, size, size+1)
                b.ReportAllocs()
                b.ResetTimer()
                for i := 0; i < b.N; i++ {
                    a = a[:size]
                    a = Insert(a, 1, len(a)/2)
                }
                b.StopTimer()
            },
        )
    }
}

输出:

$ go test -bench=.
goos: linux
goarch: amd64
pkg: sdizo
BenchmarkInsert/10k-4         50000      32502 ns/op       0 B/op      0 allocs/op
BenchmarkInsert/20k-4         20000      64364 ns/op       0 B/op      0 allocs/op
BenchmarkInsert/30k-4         20000      97310 ns/op       0 B/op      0 allocs/op
BenchmarkInsert/40k-4         10000     129196 ns/op       0 B/op      0 allocs/op
BenchmarkInsert/50k-4         10000     161427 ns/op       0 B/op      0 allocs/op
PASS
ok      so/sdizo    9.778s
$ 

现在我们可以对您的Insert算法进行基准测试,我们可以使用这些知识来改进算法。例如,

package sdizo

import (
    "strconv"
    "testing"
)

func Insert(slice []int, element int, index int) []int {
    slice = slice[:len(slice)+1]
    copy(slice[index+1:], slice[index:])
    slice[index] = element
    return slice
}

func BenchmarkInsert(b *testing.B) {
    for size := 10000; size <= 50000; size += 10000 {

        b.Run(strconv.Itoa(size/1000)+"k",

            func(b *testing.B) {
                a := make([]int, size, size+1)
                b.ReportAllocs()
                b.ResetTimer()
                for i := 0; i < b.N; i++ {
                    a = a[:size]
                    a = Insert(a, 1, len(a)/2)
                }
                b.StopTimer()
            },
        )
    }
}

输出:

$ go test -bench=.
goos: linux
goarch: amd64
pkg: sdizo
BenchmarkInsert/10k-4        200000       7664 ns/op       0 B/op      0 allocs/op
BenchmarkInsert/20k-4        100000      15208 ns/op       0 B/op      0 allocs/op
BenchmarkInsert/30k-4        100000      22959 ns/op       0 B/op      0 allocs/op
BenchmarkInsert/40k-4         50000      35181 ns/op       0 B/op      0 allocs/op
BenchmarkInsert/50k-4         50000      39658 ns/op       0 B/op      0 allocs/op
PASS
ok      so/sdizo    10.331s
$