基准测试
# 函数格式
基准测试就是在一定的工作负载之下检测程序性能的一种方法。基准测试的基本格式如下:
func BenchmarkName(b *testing.B){
// ...
}
1
2
3
2
3
基准测试以Benchmark
为前缀,需要一个*testing.B
类型的参数 b,基准测试必须要执行b.N
次,这样的测试才有对照性,b.N
的值是系统根据实际情况去调整的,从而保证测试的稳定性。 testing.B
拥有的方法如下:
func (c *B) Error(args ...interface{})
func (c *B) Errorf(format string, args ...interface{})
func (c *B) Fail()
func (c *B) FailNow()
func (c *B) Failed() bool
func (c *B) Fatal(args ...interface{})
func (c *B) Fatalf(format string, args ...interface{})
func (c *B) Log(args ...interface{})
func (c *B) Logf(format string, args ...interface{})
func (c *B) Name() string
func (b *B) ReportAllocs()
func (b *B) ResetTimer()
func (b *B) Run(name string, f func(b *B)) bool
func (b *B) RunParallel(body func(*PB))
func (b *B) SetBytes(n int64)
func (b *B) SetParallelism(p int)
func (c *B) Skip(args ...interface{})
func (c *B) SkipNow()
func (c *B) Skipf(format string, args ...interface{})
func (c *B) Skipped() bool
func (b *B) StartTimer()
func (b *B) StopTimer()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 测试示例
func BenchmarkSplit(b *testing.B) {
for i := 0; i < b.N; i++ {
SplitStr("a:b:c:d", ":")
}
}
1
2
3
4
5
2
3
4
5
然后运行测试用例:
使用代码go test -bench=Split
:
PS E:\DEV\Go\src\code.rookieops.com\day05\splitStr> go test -bench=Split
goos: windows
goarch: amd64
pkg: code.rookieops.com/day05/splitStr
BenchmarkSplit-4 2999503 409 ns/op
PASS
ok code.rookieops.com/day05/splitStr 3.633s
1
2
3
4
5
6
7
2
3
4
5
6
7
其中BenchmarkSplit-4
表示对 Split 函数进行基准测试,数字4
表示GOMAXPROCS
的值,这个对于并发基准测试很重要。2999503
和 409ns/op
表示每次调用Split
函数耗时409ns
,这个结果是2999503
次调用的平均值。
go test -bench=Split -benchmem
goos: windows
goarch: amd64
pkg: code.rookieops.com/day05/splitStr
BenchmarkSplit-4 3591652 311 ns/op 112 B/op 3 allocs/op
PASS
ok code.rookieops.com/day05/splitStr 2.219s
1
2
3
4
5
6
7
2
3
4
5
6
7
其中,112 B/op
表示每次操作内存分配了 112 字节,3 allocs/op
则表示每次操作进行了 3 次内存分配。
我们将我们的Split
函数优化如下:
package splitStr
import "strings"
// SplitStr ..
func SplitStr(s, sep string) (res []string) {
// 取索引
res = make([]string, 0, strings.Count(s, sep)+1)
index := strings.Index(s, sep)
for index >= 0 {
res = append(res, s[:index])
s = s[index+len(sep):]
index = strings.Index(s, sep)
}
res = append(res, s)
return
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
然后运行测试代码如下:
go test -bench=Split -benchmem
goos: windows
goarch: amd64
pkg: code.rookieops.com/day05/splitStr
BenchmarkSplit-4 4508462 233 ns/op 64 B/op 1 allocs/op
PASS
ok code.rookieops.com/day05/splitStr 3.234s
1
2
3
4
5
6
7
2
3
4
5
6
7
这一次我们提前使用 make 函数将 res 初始化为一个容量足够大的切片,而不再像之前一样通过调用 append 函数来追加。这样以来减少了 2/3 的内存分配次数,并且减少了一半的内存分配。
# 性能比较函数
性能比较函数是对一个函数处理不同请求的差别。
如下编写一个斐波拉契函数:
// Fib 斐波拉契函数
func Fib(n int) int {
if n < 2 {
return n
}
return Fib(n-1) + Fib(n-2)
}
1
2
3
4
5
6
7
2
3
4
5
6
7
然后我们编写性能比较函数:
func benchmarkFib(b *testing.B, n int) {
for i := 0; i < b.N; i++ {
Fib(n)
}
}
func BenchmarkFib1(b *testing.B) {
benchmarkFib(b, 1)
}
func BenchmarkFib2(b *testing.B) {
benchmarkFib(b, 2)
}
func BenchmarkFib3(b *testing.B) {
benchmarkFib(b, 3)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
运行测试用例代码如下:
PS E:\DEV\Go\src\code.rookieops.com\day05\splitStr> go test -bench=Fib1
goos: windows
goarch: amd64
pkg: code.rookieops.com/day05/splitStr
BenchmarkFib1-4 278520369 4.16 ns/op
PASS
ok code.rookieops.com/day05/splitStr 2.819s
PS E:\DEV\Go\src\code.rookieops.com\day05\splitStr> go test -bench=Fib2
goos: windows
goarch: amd64
pkg: code.rookieops.com/day05/splitStr
BenchmarkFib2-4 128817837 8.50 ns/op
PASS
ok code.rookieops.com/day05/splitStr 2.764s
PS E:\DEV\Go\src\code.rookieops.com\day05\splitStr> go test -bench=Fib3
goos: windows
goarch: amd64
pkg: code.rookieops.com/day05/splitStr
BenchmarkFib3-4 80003732 16.0 ns/op
PASS
ok code.rookieops.com/day05/splitStr 2.283s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
需要注意的是,默认情况下,每个基准测试至少运行 1 秒。如果在 Benchmark 函数返回时没有到 1 秒,则 b.N 的值会按 1,2,5,10,20,50,…增加,并且函数再次运行。
上次更新: 2025/07/18, 11:04:43