如何在 golang 基准测试中正确启动时间和停止时间?
How to properly StartTime and StopTime in golang benchmark?
我写了一个基准来测试两个斐波那契数生成器的速度,源代码是here on github。
func BenchmarkFib(b *testing.B) {
fibFuncs := []struct {
name string
f func(int) int
}{
{"recursive", fibRecu},
{"iterative", fibIter},
}
for _, fibFunc := range fibFuncs {
// calculate k'th Fibonacci number
for k := 10; k < 1001; k *= 10 {
b.Run(fmt.Sprintf("%s Fib %v", fibFunc.name, k), func(b *testing.B) {
for n := 0; n < b.N; n++ {
// b.StopTimer()
// reset the memo
memo = map[int]int{0: 0, 1: 1, 2: 1}
// b.StartTimer()
fibFunc.f(k)
}
})
}
}
}
事实上,基准测试有效,输出为
nos (master) fibonacci $ go test -bench .
goos: linux
goarch: amd64
pkg: github.com/nosarthur/dynamicP/fibonacci
BenchmarkFib/recursive_Fib_10-4 1000000 1256 ns/op
BenchmarkFib/recursive_Fib_100-4 100000 18256 ns/op
BenchmarkFib/recursive_Fib_1000-4 10000 206109 ns/op
BenchmarkFib/iterative_Fib_10-4 10000000 218 ns/op
BenchmarkFib/iterative_Fib_100-4 5000000 292 ns/op
BenchmarkFib/iterative_Fib_1000-4 2000000 881 ns/op
PASS
ok github.com/nosarthur/dynamicP/fibonacci 12.208s
但是,我添加了b.StopTime()
和b.StartTime()
来排除重置备忘录的时间。取消注释这两行,基准测试挂起,部分输出为
nos (master *) fibonacci $ go test -bench .
goos: linux
goarch: amd64
pkg: github.com/nosarthur/dynamicP/fibonacci
BenchmarkFib/recursive_Fib_10-4 1000000 2139 ns/op
BenchmarkFib/recursive_Fib_100-4 100000 24775 ns/op
BenchmarkFib/recursive_Fib_1000-4 5000 239197 ns/op
BenchmarkFib/iterative_Fib_10-4 ^Csignal: interrupt
FAIL github.com/nosarthur/dynamicP/fibonacci 269.067s
排除备忘录重置的正确方法是什么?我的go版本是1.10.1
发生的事情是你的函数真的很快,特别是在迭代函数的情况下,你的地图重置(以及 StartTimer
和 StopTimer
函数本身与 运行 时间统计分配)要慢得多。
所以发生的事情是,当您调用 StopTimer
时,它会将基准测试的内部跟踪持续时间设置为仅 运行 函数所花费的时间。猜猜它是如何估计在指定的基准时间内 运行 的迭代次数?是的,您猜对了 - 内部持续时间。
所以基本上,您的迭代函数需要大约 10ns 到 运行,地图重置大约需要 250ns,而 Timer 函数需要相当长的时间 - 但基准估计每个 运行 只需要20ns 并相应地设置迭代次数。
我的建议 - 在这种情况下不要使用 StartTimer/StopTimer
函数,只需添加第三个 运行 你的非操作测试 - 基本上:
{"baseline", func(int) int {return 0}},
然后只需从其他两组中减去此函数的时间,即可估计 ns/op 有多少来自分配与函数本身。
这是我 运行 的结果:
BenchmarkFib/baseline_Fib_10-2 5000000 357 ns/op
BenchmarkFib/baseline_Fib_100-2 5000000 327 ns/op
BenchmarkFib/baseline_Fib_1000-2 5000000 310 ns/op
BenchmarkFib/recursive_Fib_10-2 1000000 1659 ns/op
BenchmarkFib/recursive_Fib_100-2 50000 24898 ns/op
BenchmarkFib/recursive_Fib_1000-2 5000 301771 ns/op
BenchmarkFib/iterative_Fib_10-2 5000000 333 ns/op
BenchmarkFib/iterative_Fib_100-2 3000000 394 ns/op
BenchmarkFib/iterative_Fib_1000-2 1000000 1052 ns/op
PASS
ok _/tmp/dynamicP/fibonacci 15.305s
我写了一个基准来测试两个斐波那契数生成器的速度,源代码是here on github。
func BenchmarkFib(b *testing.B) {
fibFuncs := []struct {
name string
f func(int) int
}{
{"recursive", fibRecu},
{"iterative", fibIter},
}
for _, fibFunc := range fibFuncs {
// calculate k'th Fibonacci number
for k := 10; k < 1001; k *= 10 {
b.Run(fmt.Sprintf("%s Fib %v", fibFunc.name, k), func(b *testing.B) {
for n := 0; n < b.N; n++ {
// b.StopTimer()
// reset the memo
memo = map[int]int{0: 0, 1: 1, 2: 1}
// b.StartTimer()
fibFunc.f(k)
}
})
}
}
}
事实上,基准测试有效,输出为
nos (master) fibonacci $ go test -bench .
goos: linux
goarch: amd64
pkg: github.com/nosarthur/dynamicP/fibonacci
BenchmarkFib/recursive_Fib_10-4 1000000 1256 ns/op
BenchmarkFib/recursive_Fib_100-4 100000 18256 ns/op
BenchmarkFib/recursive_Fib_1000-4 10000 206109 ns/op
BenchmarkFib/iterative_Fib_10-4 10000000 218 ns/op
BenchmarkFib/iterative_Fib_100-4 5000000 292 ns/op
BenchmarkFib/iterative_Fib_1000-4 2000000 881 ns/op
PASS
ok github.com/nosarthur/dynamicP/fibonacci 12.208s
但是,我添加了b.StopTime()
和b.StartTime()
来排除重置备忘录的时间。取消注释这两行,基准测试挂起,部分输出为
nos (master *) fibonacci $ go test -bench .
goos: linux
goarch: amd64
pkg: github.com/nosarthur/dynamicP/fibonacci
BenchmarkFib/recursive_Fib_10-4 1000000 2139 ns/op
BenchmarkFib/recursive_Fib_100-4 100000 24775 ns/op
BenchmarkFib/recursive_Fib_1000-4 5000 239197 ns/op
BenchmarkFib/iterative_Fib_10-4 ^Csignal: interrupt
FAIL github.com/nosarthur/dynamicP/fibonacci 269.067s
排除备忘录重置的正确方法是什么?我的go版本是1.10.1
发生的事情是你的函数真的很快,特别是在迭代函数的情况下,你的地图重置(以及 StartTimer
和 StopTimer
函数本身与 运行 时间统计分配)要慢得多。
所以发生的事情是,当您调用 StopTimer
时,它会将基准测试的内部跟踪持续时间设置为仅 运行 函数所花费的时间。猜猜它是如何估计在指定的基准时间内 运行 的迭代次数?是的,您猜对了 - 内部持续时间。
所以基本上,您的迭代函数需要大约 10ns 到 运行,地图重置大约需要 250ns,而 Timer 函数需要相当长的时间 - 但基准估计每个 运行 只需要20ns 并相应地设置迭代次数。
我的建议 - 在这种情况下不要使用 StartTimer/StopTimer
函数,只需添加第三个 运行 你的非操作测试 - 基本上:
{"baseline", func(int) int {return 0}},
然后只需从其他两组中减去此函数的时间,即可估计 ns/op 有多少来自分配与函数本身。
这是我 运行 的结果:
BenchmarkFib/baseline_Fib_10-2 5000000 357 ns/op
BenchmarkFib/baseline_Fib_100-2 5000000 327 ns/op
BenchmarkFib/baseline_Fib_1000-2 5000000 310 ns/op
BenchmarkFib/recursive_Fib_10-2 1000000 1659 ns/op
BenchmarkFib/recursive_Fib_100-2 50000 24898 ns/op
BenchmarkFib/recursive_Fib_1000-2 5000 301771 ns/op
BenchmarkFib/iterative_Fib_10-2 5000000 333 ns/op
BenchmarkFib/iterative_Fib_100-2 3000000 394 ns/op
BenchmarkFib/iterative_Fib_1000-2 1000000 1052 ns/op
PASS
ok _/tmp/dynamicP/fibonacci 15.305s