目錄
1、基本使用
2、bench 的工作原理
3、傳入 cpu num 進行測試
4、count 多次運行基準測試
5、benchtime 指定運行秒數
6、ResetTimer 重置定時器
7、benchmem 展示內存消耗
1、基本使用基準測試常用於代碼性能測試,函數需要導入testing包,並定義以Benchmark開頭的函數, 參數為testing.B指針類型,在測試函數中循環調用函數多次
go test testcalc/calc -bench .
go test testcalc/calc -bench . -run=none
# 顯示內存信息
go test testcalc/calc -bench . -benchmem
go test -bench=. -benchmem -run=nonego test會在運行基準測試之前之前執行包裡所有的單元測試,所有如果你的包裡有很多單元測試,或者它們會運行很長時間,你也可以通過go test的-run標識排除這些單元測試
業務代碼fib.go,測試斐波那契數列
package pkg06
func fib(n int) int {
if n == 0 || n == 1 {
return n
}
return fib(n-2) + fib(n-1)
}測試代碼fib_test.go
package pkg06
import "testing"
func BenchmarkFib(b *testing.B) {
for n := 0; n < b.N; n++ {
fib(30)
}
}執行測試
➜ go test -bench=. -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12 250 4682682 ns/op
PASS
ok pkg06 1.875s
➜ go test -bench=. -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12 249 4686452 ns/op 0 B/op 0 allocs/op
PASS
ok pkg06 1.854s
2、bench 的工作原理基準測試函數會被一直調用直到b.N無效,它是基準測試循環的次數b.N從1開始,如果基準測試函數在1秒內就完成 (默認值),則b.N增加,並再次運行基準測試函數b.N的值會按照序列1,2,5,10,20,50,...增加,同時再次運行基準測測試函數上述結果解讀代表1秒內運行了250次,每次4682682 ns-12後綴和用於運行次測試的GOMAXPROCS值有關。與GOMAXPROCS一樣,此數字默認為啟動時Go進程可見的CPU數。可以使用-cpu標識更改此值,可以傳入多個值以列表形式來運行基準測試3、傳入 cpu num 進行測試➜ go test -bench=. -cpu=1,2,4 -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib 244 4694667 ns/op 0 B/op 0 allocs/op
BenchmarkFib-2 255 4721201 ns/op 0 B/op 0 allocs/op
BenchmarkFib-4 256 4756392 ns/op 0 B/op 0 allocs/op
PASS
ok pkg06 5.826s
4、count 多次運行基準測試因為熱縮放、內存局部性、後臺處理、gc活動等等會導致單次的誤差,所以一般會進行多次測試
➜ go test -bench=. -count=10 -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12 217 5993577 ns/op 0 B/op 0 allocs/op
BenchmarkFib-12 246 5065577 ns/op 0 B/op 0 allocs/op
BenchmarkFib-12 244 4955397 ns/op 0 B/op 0 allocs/op
BenchmarkFib-12 255 4689529 ns/op 0 B/op 0 allocs/op
BenchmarkFib-12 254 4879802 ns/op 0 B/op 0 allocs/op
BenchmarkFib-12 254 4691213 ns/op 0 B/op 0 allocs/op
BenchmarkFib-12 255 4772108 ns/op 0 B/op 0 allocs/op
BenchmarkFib-12 240 4724141 ns/op 0 B/op 0 allocs/op
BenchmarkFib-12 255 4717087 ns/op 0 B/op 0 allocs/op
BenchmarkFib-12 255 4787803 ns/op 0 B/op 0 allocs/op
PASS
ok pkg06 18.166s
5、benchtime 指定運行秒數有的函數比較慢,為了更精確的結果,可以通過-benchtime標誌指定運行時間,從而使它運行更多次
➜ go test -bench=. -benchtime=5s -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12 1128 4716535 ns/op 0 B/op 0 allocs/op
PASS
ok pkg06 7.199s
6、ResetTimer 重置定時器可能在真正測試之前還需要做很多例如初始化等工作,這時可以在需要測試的函數執行之初添加一個重置定時器的功能,這樣最終得到的時間就更為精確
package pkg06
import (
"testing"
"time"
)
func BenchmarkFib(b *testing.B) {
time.Sleep(3 * time.Second)
b.ResetTimer()
for n := 0; n < b.N; n++ {
fib(30)
}
}執行測試
➜ go test -bench=. -benchtime=5s -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg06
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkFib-12 1239 4712413 ns/op 0 B/op 0 allocs/op
PASS
ok pkg06 16.122s
7、benchmem 展示內存消耗例如測試大cap的切片,直接用cap初始化和cap動態擴容進行對比package pkg07
import (
"math/rand"
"testing"
"time"
)
// 指定大的cap的切片
func generateWithCap(n int) []int {
rand.Seed(time.Now().UnixNano())
nums := make([]int, 0, n)
for i := 0; i < n; i++ {
nums = append(nums, rand.Int())
}
return nums
}
// 動態擴容的slice
func generateDynamic(n int) []int {
rand.Seed(time.Now().UnixNano())
nums := make([]int, 0)
for i := 0; i < n; i++ {
nums = append(nums, rand.Int())
}
return nums
}
func BenchmarkGenerateWithCap(b *testing.B) {
for n := 0; n < b.N; n++ {
generateWithCap(100000)
}
}
func BenchmarkGenerateDynamic(b *testing.B) {
for n := 0; n < b.N; n++ {
generateDynamic(100000)
}
}執行測試
➜ go test -bench=. -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg07
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkGenerateWithCap-12 672 1729465 ns/op 802817 B/op 1 allocs/op
BenchmarkGenerateDynamic-12 561 2122992 ns/op 4654346 B/op 30 allocs/op
PASS
ok pkg07 3.777s結論:用cap初始化好的性能可以高一個數據量級
例如測試測試函數複雜度,不帶cap的slice動態擴容對上面代碼中調用動態擴容生成切片進行再次封裝
package pkg08
import (
"math/rand"
"testing"
"time"
)
// 指定大的cap的切片
func generateWithCap(n int) []int {
rand.Seed(time.Now().UnixNano())
nums := make([]int, 0, n)
for i := 0; i < n; i++ {
nums = append(nums, rand.Int())
}
return nums
}
// 動態擴容的slice
func generateDynamic(n int) []int {
rand.Seed(time.Now().UnixNano())
nums := make([]int, 0)
for i := 0; i < n; i++ {
nums = append(nums, rand.Int())
}
return nums
}
func benchmarkGenerate(i int, b *testing.B) {
for n := 0; n < b.N; n++ {
generateDynamic(i)
}
}
func BenchmarkGenerateDynamic1000(b *testing.B) { benchmarkGenerate(1000, b) }
func BenchmarkGenerateDynamic10000(b *testing.B) { benchmarkGenerate(10000, b) }
func BenchmarkGenerateDynamic100000(b *testing.B) { benchmarkGenerate(100000, b) }
func BenchmarkGenerateDynamic1000000(b *testing.B) { benchmarkGenerate(1000000, b) }
func BenchmarkGenerateDynamic10000000(b *testing.B) { benchmarkGenerate(10000000, b) }執行測試
➜ go test -bench=. -benchmem -run=none
goos: darwin
goarch: amd64
pkg: pkg08
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkGenerateDynamic1000-12 39540 26557 ns/op 16376 B/op 11 allocs/op
BenchmarkGenerateDynamic10000-12 5452 210894 ns/op 386296 B/op 20 allocs/op
BenchmarkGenerateDynamic100000-12 572 2106325 ns/op 4654341 B/op 30 allocs/op
BenchmarkGenerateDynamic1000000-12 48 23070939 ns/op 45188416 B/op 40 allocs/op
BenchmarkGenerateDynamic10000000-12 5 212567041 ns/op 423503110 B/op 50 allocs/op
PASS
ok pkg08 9.686s結論:輸入變為原來的10倍,單次耗時也差不多是上一級的10倍。說明這個函數的複雜度是接近線性的
See you ~