所以我决定在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是否有方便的方法将基准测试结果写入文件?
答案 0 :(得分:1)
当您进行基准测试时,实际上会对您想要的基准进行基准测试你的用例不是make,然后每次都插入,所以只需make一次,然后在循环中测试insert。关键是只测试阻塞点。
您想要做的只是测试Insert
。你可以通过简单地每次调整数组大小来实现这一点,其成本基本上没有(至少与make
或Insert
相比)。
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
答案 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
$