Golang基準測試
-
- 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=none

go 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
倍。說明這個函式的複雜度是接近線性的
「其他文章」
- Gradle打包工具入門
- 服務網格和Istio初識-續
- 服務網格和Istio初識
- Golang與非對稱加密
- ack叢集Terway網路場景下的vSwitch擴容
- Golang與對稱加密
- 基於ack k8s叢集排程的方案設計
- 基於Dockerfile構建容器映象的最佳實踐
- Golang反射-下篇
- Golang反射-上篇
- Azure DevOps的使用入門
- Golang介面型別-下篇
- Golang介面型別-上篇
- 基於Python實現原生的登入驗證碼
- Golang開發命令列工具之flag包的使用
- Golang檔案操作-下篇
- k8s環境下處理容器時間問題的多種姿勢
- Golang基準測試
- 淺談Prometheus的資料儲存
- Golang單元測試