上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
12.2 基准测试
有时也称作性能测试。目的是获知算法执行时间,及内存开销。
- 保存在
_test.go
文件中。 - 函数以
Benchmark
为前缀, - 类型
B
与T
方法类似,省略。
- 以
go test -bench
执行。 - 仅执行性能测试,可用
-run NONE
忽略单元测试。
func BenchmarkAdd(b *testing.B) { for i := 0; i < b.N; i++ { add(1, 2) } }
$ go test -v -bench . -run None ./mylib goos: linux goarch: amd64 pkg: test/mylib cpu: Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz BenchmarkAdd BenchmarkAdd-2 1000000000 0.3534 ns/op PASS ok test/mylib 0.417s
-bench
:指定要运行的测试。(正则表达式,-bench .
)-benchtime
:单次测试运行时间或循环次数。(默认 1s,1m20s,100x)-count
:执行几轮测试。(benchtime * count)-cpu
:测试所用 CPU 核心数。(-cpu 1,2,4
执行三轮测试)-list
: 列出测试函数,不执行。-benchmem
:显示内存分配(堆)信息。
$ go test -v -run None -bench Add -count 2 ./mylib BenchmarkAdd-2 1000000000 0.3591 ns/op BenchmarkAdd-2 1000000000 0.3588 ns/op
$ go test -v -run None -bench Add -cpu 1,2,4 ./mylib BenchmarkAdd 1000000000 0.3583 ns/op BenchmarkAdd-2 1000000000 0.3594 ns/op BenchmarkAdd-4 1000000000 0.3552 ns/op
如果执行次数足够多,则 benchtime
设置的时长无效。
对于某些耗时的目标,设置足够长的时间或次数,以便有足够取样获取平均值。
func BenchmarkSleep(b *testing.B) { for i := 0; i < b.N; i++ { time.Sleep(time.Second) } }
$ go test -v -run None -bench Add -benchtime 10s ./mylib BenchmarkAdd BenchmarkAdd-2 1000000000 0.3609 ns/op PASS ok test/mylib 0.440s $ go test -v -run None -bench Sleep -benchtime 10s ./mylib BenchmarkSleep BenchmarkSleep-2 10 1003276590 ns/op PASS ok test/mylib 11.048s
内部实现
内部通过增加循环次数,直到取样(时间或次数上限)足够,以获得最佳平均值。
func BenchmarkAdd(b *testing.B) { println("b.N = ", b.N) for i := 0; i < b.N; i++ { add(1, 2) } }
$ go test -v -run None -bench Add ./mylib BenchmarkAdd b.N = 1 b.N = 100 b.N = 10000 b.N = 1000000 b.N = 100000000 b.N = 1000000000 BenchmarkAdd-2 1000000000 0.3560 ns/op PASS ok test/mylib 0.425s
决定循环次数( b.N
)的因素,按优先级次序:
- 手工指定次数(
-benchtime 10x
)。 - 内部次数上限(
1e9
,1000000000
)。 - 手工指定时长(
-benchtime 10s
)。
另外,性能测试会执行 runtime.GC
清理现场,以确保测试结果不受干扰。
// benchmark.go func (b *B) run() { b.doBench() } func (b *B) doBench() BenchmarkResult { go b.launch() <-b.signal return b.result } func (b *B) launch() { defer func() { b.signal <- true }() // 指定次数。 if b.benchTime.n > 0 { if b.benchTime.n > 1 { b.runN(b.benchTime.n) } } else { // 指定时长,默认 1 秒。 d := b.benchTime.d // 时间不够,且次数没有超出上限,增加循环次数重来。 // 提示,b.duration 由 StopTimer 更新。 for n := int64(1); !b.failed && b.duration < d && n < 1e9; { last := n // 重新计算循环次数。 n = goalns * prevIters / prevns n += n / 5 n = min(n, 100*last) n = max(n, last+1) ... // 次数上限。 n = min(n, 1e9) b.runN(int(n)) } } b.result = BenchmarkResult{b.N, b.duration, ...} } func (b *B) runN(n int) { // Try to get a comparable environment for each run // by clearing garbage from previous runs. runtime.GC() b.ResetTimer() b.StartTimer() b.benchFunc(b) // 测试函数。 b.StopTimer() }
// benchmark.go func (b *B) StartTimer() { b.start = time.Now() } func (b *B) StopTimer() { b.duration += time.Since(b.start) }
// benchmark.go type BenchmarkResult struct { N int // The number of iterations. T time.Duration // The total time taken. } func (r BenchmarkResult) NsPerOp() int64 { return r.T.Nanoseconds() / int64(r.N) }
子测试
操作与 T
基本一致。但没有 Parallel
,而是 RunParallel
(详见后文)。
每次执行都会调用
runtime.GC
清理现场,以减少外部干扰,更别说多个子测试并发了。
func BenchmarkSubs(b *testing.B) { b.Log("setup") b.Cleanup(func(){ b.Log("cleanup") }) b.Run("A", BenchmarkA) b.Run("B", BenchmarkB) b.Run("C", BenchmarkC) }
$ go test -v -run None -bench Subs ./mylib BenchmarkSubs add_test.go:33: setup BenchmarkSubs/A BenchmarkSubs/A-2 1 1000442000 ns/op BenchmarkSubs/B BenchmarkSubs/B-2 1 1002897400 ns/op BenchmarkSubs/C BenchmarkSubs/C-2 1 1003405600 ns/op add_test.go:34: cleanup PASS ok test/mylib 3.036s $ go test -v -run None -bench Subs/B ./mylib BenchmarkSubs add_test.go:33: setup BenchmarkSubs/B BenchmarkSubs/B-2 1 1001120200 ns/op add_test.go:34: cleanup PASS ok test/mylib 1.029s
计时器
计时器默认自动处理。如测试逻辑中有需要排除的因素,可手工调用。
func BenchmarkTimer(b *testing.B) { // setup time.Sleep(time.Second) // teardown defer time.Sleep(time.Second) // 重置计时器,避免 setup 干扰。 b.ResetTimer() for i := 0; i < b.N; i++ { add(1, 2) } // 停止计时器,避免 teardown 干扰。 b.StopTimer() }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论