基准測試
Go語言標准庫內置的 testing 測試框架提供了基准測試(benchmark)的能力,實現了對某個特定目標場景的某項性能指標進行定量的和可對比的測試。
基本規則
- 基准測試的代碼文件必須以_test.go結尾
- 基准測試的函數必須以Benchmark開頭,必須是可導出的
- 基准測試函數必須接受一個指向Benchmark類型的指針作為唯一參數
- b.ResetTimer是重置計時器,這樣可以避免for循環之前的初始化代碼的干擾
- 最后的for循環很重要,被測試的代碼要放到循環里
- b.N是基准測試框架提供的,表示循環的次數,因為需要反復調用測試的代碼,才可以評估性能
常用命令
$ go test -bench=. -benchmem
- 使用
-bench=.
標記,它接受一個表達式作為參數,匹配基准測試的函數,.
表示運行所有基准測試。 - 使用
-benchmem
提供每次操作分配內存的次數,以及每次操作分配的字節數。 - 使用
-run=none
匹配單元測試方法,這里使用none
是因為沒有這個名字的單元測試方法,等效於過濾掉單元測試的輸出。 - 使用
-benchtime=3s
指定測試的時間,例如3秒,測試時間默認是1秒。 - 使用
-count=3
用來設置 benchmark 的輪數。例如,進行3輪benchmark。
舉個栗子
評估1000個字符串的連接性能,分別使用 +
操作符、bytes.Buffer
和strings.Builder
進行測試。
package main
import (
"bytes"
"strings"
"testing"
)
var strLen = 1000
func BenchmarkConcatString(b *testing.B) {
var str string
i := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
str += "x"
i++
if i >= strLen {
i = 0
str = ""
}
}
}
func BenchmarkConcatBuffer(b *testing.B) {
var buffer bytes.Buffer
i := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
buffer.WriteString("x")
i++
if i >= strLen {
i = 0
buffer = bytes.Buffer{}
}
}
}
在終端運行命令 go test -bench=. -benchmem
,輸入如下信息:
$ go test -bench=^BenchmarkConcat -benchmem
goos: darwin
goarch: amd64
pkg: go_learning_notes/benchmark
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkConcatString-4 8040235 140.5 ns/op 530 B/op 0 allocs/op
BenchmarkConcatBuffer-4 168521010 6.384 ns/op 2 B/op 0 allocs/op
BenchmarkConcatBuilder-4 464368256 2.475 ns/op 2 B/op 0 allocs/op
PASS
ok go_learning_notes/benchmark 5.035s
通過上面的指標信息,可以得出結論 bytes.Buffer
和strings.Builder
比 +
操作符 更快。
參考文章
- Practical Go Benchmarks:https://www.instana.com/blog/practical-golang-benchmarks/
- Benchmarks of Go serialization methods:https://github.com/alecthomas/go_serialization_benchmarks
- Debugging performance issues in Go programs:https://github.com/golang/go/wiki/Performance