Golang基準(zhǔn)測試
目錄
1、基本使用
2、bench 的工作原理
3、傳入 cpu num 進(jìn)行測試
4、count 多次運(yùn)行基準(zhǔn)測試
5、benchtime 指定運(yùn)行秒數(shù)
6、ResetTimer 重置定時(shí)器
7、benchmem 展示內(nèi)存消耗

1、基本使用
基準(zhǔn)測試常用于代碼性能測試,函數(shù)需要導(dǎo)入testing包,并定義以Benchmark開頭的函數(shù), 參數(shù)為testing.B指針類型,在測試函數(shù)中循環(huán)調(diào)用函數(shù)多次
go test testcalc/calc -bench .
go test testcalc/calc -bench . -run=none
# 顯示內(nèi)存信息
go test testcalc/calc -bench . -benchmem
go test -bench=. -benchmem -run=none

go test會(huì)在運(yùn)行基準(zhǔn)測試之前之前執(zhí)行包里所有的單元測試,所有如果你的包里有很多單元測試,或者它們會(huì)運(yùn)行很長時(shí)間,你也可以通過go test的-run標(biāo)識(shí)排除這些單元測試
業(yè)務(wù)代碼fib.go,測試斐波那契數(shù)列
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)
}
}
執(zhí)行測試
? 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 的工作原理
基準(zhǔn)測試函數(shù)會(huì)被一直調(diào)用直到 b.N無效,它是基準(zhǔn)測試循環(huán)的次數(shù)b.N從1開始,如果基準(zhǔn)測試函數(shù)在1秒內(nèi)就完成 (默認(rèn)值),則b.N增加,并再次運(yùn)行基準(zhǔn)測試函數(shù)b.N的值會(huì)按照序列1,2,5,10,20,50,...增加,同時(shí)再次運(yùn)行基準(zhǔn)測測試函數(shù)上述結(jié)果解讀代表 1秒內(nèi)運(yùn)行了250次,每次4682682 ns-12后綴和用于運(yùn)行次測試的GOMAXPROCS值有關(guān)。與GOMAXPROCS一樣,此數(shù)字默認(rèn)為啟動(dòng)時(shí)Go進(jìn)程可見的CPU數(shù)。可以使用-cpu標(biāo)識(shí)更改此值,可以傳入多個(gè)值以列表形式來運(yùn)行基準(zhǔn)測試
3、傳入 cpu num 進(jìn)行測試
? 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 多次運(yùn)行基準(zhǔn)測試
因?yàn)闊峥s放、內(nèi)存局部性、后臺(tái)處理、gc活動(dòng)等等會(huì)導(dǎo)致單次的誤差,所以一般會(huì)進(jìn)行多次測試
? 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 指定運(yùn)行秒數(shù)
有的函數(shù)比較慢,為了更精確的結(jié)果,可以通過-benchtime標(biāo)志指定運(yùn)行時(shí)間,從而使它運(yùn)行更多次
? 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 重置定時(shí)器
可能在真正測試之前還需要做很多例如初始化等工作,這時(shí)可以在需要測試的函數(shù)執(zhí)行之初添加一個(gè)重置定時(shí)器的功能,這樣最終得到的時(shí)間就更為精確
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)
}
}
執(zhí)行測試
? 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 展示內(nèi)存消耗
例如測試大 cap的切片,直接用cap初始化和cap動(dòng)態(tài)擴(kuò)容進(jìn)行對比
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
}
// 動(dòng)態(tài)擴(kuò)容的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)
}
}
執(zhí)行測試
? 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
結(jié)論:用cap初始化好的性能可以高一個(gè)數(shù)據(jù)量級(jí)
例如測試測試函數(shù)復(fù)雜度,不帶 cap的slice動(dòng)態(tài)擴(kuò)容
對上面代碼中調(diào)用動(dòng)態(tài)擴(kuò)容生成切片進(jìn)行再次封裝
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
}
// 動(dòng)態(tài)擴(kuò)容的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) }
執(zhí)行測試
? 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
結(jié)論:輸入變?yōu)樵瓉淼?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">10倍,單次耗時(shí)也差不多是上一級(jí)的10倍。說明這個(gè)函數(shù)的復(fù)雜度是接近線性的
See you ~
