基准测试 #
基准测试(Benchmark)用于测量代码的性能。Go语言的 testing 包内置了基准测试支持,可以方便地进行性能分析。
基本基准测试 #
编写基准测试 #
基准测试函数以 Benchmark 开头,参数为 *testing.B:
go
package myapp
import "testing"
func Fibonacci(n int) int {
if n <= 1 {
return n
}
return Fibonacci(n-1) + Fibonacci(n-2)
}
func BenchmarkFibonacci(b *testing.B) {
for i := 0; i < b.N; i++ {
Fibonacci(20)
}
}
运行基准测试 #
bash
go test -bench=.
go test -bench=Fibonacci
go test -bench=. -benchmem
输出示例:
text
BenchmarkFibonacci-8 30000 45123 ns/op
30000- 运行次数45123 ns/op- 每次操作耗时(纳秒)
理解 b.N #
b.N 是基准测试框架自动调整的迭代次数,确保测试运行足够长的时间:
go
package myapp
import "testing"
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
框架会自动增加 b.N 直到获得稳定的测量结果。
基准测试选项 #
控制运行时间 #
bash
go test -bench=. -benchtime=5s
go test -bench=. -benchtime=100x
内存分析 #
bash
go test -bench=. -benchmem
输出示例:
text
BenchmarkMakeSlice-8 1000000 1234 ns/op 1024 B/op 1 allocs/op
1024 B/op- 每次操作分配的内存1 allocs/op- 每次操作的内存分配次数
子基准测试 #
go
package myapp
import (
"sort"
"testing"
)
func BenchmarkSort(b *testing.B) {
b.Run("Small", func(b *testing.B) {
data := make([]int, 10)
for i := 0; i < b.N; i++ {
sort.Ints(data)
}
})
b.Run("Medium", func(b *testing.B) {
data := make([]int, 1000)
for i := 0; i < b.N; i++ {
sort.Ints(data)
}
})
b.Run("Large", func(b *testing.B) {
data := make([]int, 100000)
for i := 0; i < b.N; i++ {
sort.Ints(data)
}
})
}
重置计时器 #
当基准测试需要准备数据时,使用 ResetTimer 排除准备时间:
go
package myapp
import "testing"
func BenchmarkWithSetup(b *testing.B) {
data := make([]int, 1000000)
for i := range data {
data[i] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
process(data)
}
}
停止和恢复计时器 #
go
package myapp
import "testing"
func BenchmarkStopTimer(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
data := prepareData()
b.StartTimer()
process(data)
}
}
并行基准测试 #
使用 RunParallel 测试并发性能:
go
package myapp
import (
"sync"
"testing"
)
var pool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
func BenchmarkPool(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
buf := pool.Get().([]byte)
processBuffer(buf)
pool.Put(buf)
}
})
}
控制并发数 #
bash
go test -bench=. -cpu=1,2,4,8
内存分配优化 #
比较不同实现 #
go
package myapp
import (
"strings"
"testing"
)
func concatPlus(n int) string {
var s string
for i := 0; i < n; i++ {
s += "x"
}
return s
}
func concatBuilder(n int) string {
var builder strings.Builder
for i := 0; i < n; i++ {
builder.WriteString("x")
}
return builder.String()
}
func BenchmarkConcatPlus(b *testing.B) {
for i := 0; i < b.N; i++ {
concatPlus(1000)
}
}
func BenchmarkConcatBuilder(b *testing.B) {
for i := 0; i < b.N; i++ {
concatBuilder(1000)
}
}
运行结果:
text
BenchmarkConcatPlus-8 5000 280000 ns/op 528384 B/op 999 allocs/op
BenchmarkConcatBuilder-8 500000 3200 ns/op 512 B/op 1 allocs/op
预分配优化 #
go
package myapp
import "testing"
func makeSliceWithoutCap(n int) []int {
var result []int
for i := 0; i < n; i++ {
result = append(result, i)
}
return result
}
func makeSliceWithCap(n int) []int {
result := make([]int, 0, n)
for i := 0; i < n; i++ {
result = append(result, i)
}
return result
}
func BenchmarkSliceWithoutCap(b *testing.B) {
for i := 0; i < b.N; i++ {
makeSliceWithoutCap(10000)
}
}
func BenchmarkSliceWithCap(b *testing.B) {
for i := 0; i < b.N; i++ {
makeSliceWithCap(10000)
}
}
比较基准测试结果 #
使用 benchstat 工具比较不同版本的性能:
bash
go install golang.org/x/perf/cmd/benchstat@latest
go test -bench=. -count=5 > old.txt
go test -bench=. -count=5 > new.txt
benchstat old.txt new.txt
实用示例:JSON处理性能 #
go
package myapp
import (
"encoding/json"
"testing"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
var userJSON = []byte(`{"id":1,"name":"张三","email":"zhangsan@example.com"}`)
func BenchmarkJSONUnmarshal(b *testing.B) {
var user User
for i := 0; i < b.N; i++ {
json.Unmarshal(userJSON, &user)
}
}
func BenchmarkJSONMarshal(b *testing.B) {
user := User{ID: 1, Name: "张三", Email: "zhangsan@example.com"}
for i := 0; i < b.N; i++ {
json.Marshal(user)
}
}
实用示例:Map性能对比 #
go
package myapp
import (
"sync"
"testing"
)
func BenchmarkSyncMap(b *testing.B) {
var m sync.Map
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
key := string(rune(i % 100))
m.Store(key, i)
m.Load(key)
i++
}
})
}
func BenchmarkMutexMap(b *testing.B) {
m := make(map[string]int)
var mu sync.Mutex
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
key := string(rune(i % 100))
mu.Lock()
m[key] = i
_ = m[key]
mu.Unlock()
i++
}
})
}
小结 #
| 命令 | 说明 |
|---|---|
go test -bench=. |
运行所有基准测试 |
go test -benchmem |
显示内存分配信息 |
go test -benchtime=5s |
设置运行时间 |
go test -cpu=1,2,4 |
测试不同CPU核心数 |
b.ResetTimer() |
重置计时器 |
b.RunParallel() |
并行基准测试 |
基准测试是性能优化的基础,通过测量可以找到真正的性能瓶颈,避免过早优化。