1.数据结构:
源码位置:src/testing/benchmark.go:B
type B struct {
common
importPath string // import path of the package containing the benchmark
bstate *benchState
N int
previousN int // number of iterations in the previous run
previousDuration time.Duration // total duration of the previous run
benchFunc func(b *B)
benchTime durationOrCountFlag
bytes int64
missingBytes bool // one of the subbenchmarks does not have bytes set.
timerOn bool
showAllocResult bool
result BenchmarkResult
parallelism int // RunParallel creates parallelism*GOMAXPROCS goroutines
// The initial states of memStats.Mallocs and memStats.TotalAlloc.
startAllocs uint64
startBytes uint64
// The net total of this test after being run.
netAllocs uint64
netBytes uint64
// Extra metrics collected by ReportMetric.
extra map[string]float64
// For Loop() to be executed in benchFunc.
// Loop() has its own control logic that skips the loop scaling.
// See issue #61515.
loopN int
}
1).基础标识与调度:
importPath:包导入路径.
bstate:状态测试管理器.
benchFunc:测试函数.
2).循环控制:
N:循环执行次数.
previousN:上一轮循环次数.
loopN:loop专用循环数.
3).耗时统计:
benchTime:测试目标耗时. 控制基准测试的最小总耗时(默认 1s,可通过 -
benchtime 调整,如 -benchtime=5s 或 -benchtime=100x )
previousDuration:上一轮总耗时.
timerOn:计时器开关.
4).内存或字节统计:
bytes:每次迭代处理的字节数.
missingBytes:测试字节数缺失标记.
startAllocs:内存分布次数初始值.
startBytes:内存分配字节初始值.
netAllocs:总分配次数.
netBytes:总分配字节数.
5).并行控制:
parallelism:并行度.
6).结果与输出:
result:基准测试结果.
showAllocResult:内存结果显示标记.
extra:自定义指标.
7).通用能力:
common:通用测试能力.
2.启动计时.B.StartTimer():
源码位置:src/testing/benchmark.go
func (b *B) StartTimer() {
if !b.timerOn {
runtime.ReadMemStats(&memStats)
b.startAllocs = memStats.Mallocs
b.startBytes = memStats.TotalAlloc
b.start = highPrecisionTimeNow()
b.timerOn = true
}
}
StartTimer()负责启动计时并初始化内存相关计数.测试执行时会自动调用.一般不需
要用户启动.
流程图:
3.停止计时.B.StopTimer():
源码位置:src/testing/benchmark.go
StopTimer()负责停止计时.并累加相应的值.
// StopTimer stops timing a test. This can be used to pause the timer
// while performing steps that you don't want to measure.
func (b *B) StopTimer() {
if b.timerOn {
b.duration += highPrecisionTimeSince(b.start)
runtime.ReadMemStats(&memStats)
b.netAllocs += memStats.Mallocs - b.startAllocs
b.netBytes += memStats.TotalAlloc - b.startBytes
b.timerOn = false
}
}
需要注意的是.StopTimer()并不一定是测试结束.一个测试中有可能有多个统计阶
段.所以统计值是累加的.
流程图:
4.重置计时.B.ResetTimer():
ResetTimer()用于重置计时器.相应的也会把其他统计值也重置.
源码位置:src/testing/benchmark.go
// ResetTimer zeroes the elapsed benchmark time and memory allocation counters
// and deletes user-reported metrics.
// It does not affect whether the timer is running.
func (b *B) ResetTimer() {
if b.extra == nil {
// Allocate the extra map before reading memory stats.
// Pre-size it to make more allocation unlikely.
b.extra = make(map[string]float64, 16)
} else {
clear(b.extra)
}
if b.timerOn {
runtime.ReadMemStats(&memStats)
b.startAllocs = memStats.Mallocs
b.startBytes = memStats.TotalAlloc
b.start = highPrecisionTimeNow()
}
b.duration = 0
b.netAllocs = 0
b.netBytes = 0
}
流程图:
5.SetBytes():
源码位置:src/testing/benchmark.go
// SetBytes records the number of bytes processed in a single operation.
// If this is called, the benchmark will report ns/op and MB/s.
func (b *B) SetBytes(n int64) { b.bytes = n }
流程图:
核心公式:
// 总处理字节数 = 单次迭代字节数 × 总迭代次数
totalBytes = b.bytes × b.N
// 总耗时(秒)= 总有效耗时(纳秒) / 10^9
totalSeconds = b.duration / 1e9
// 吞吐量(MB/s)= 总处理字节数(MB) / 总耗时(秒)
// 注:1MB = 1024×1024 = 1048576 字节
throughputMBps = (totalBytes / 1048576) / totalSeconds
测试代码:
func BenchmarkSetBytes(b *testing.B) {
b.SetBytes(1024 * 1024)
for i := 0; i < b.N; i++ {
time.Sleep(1 * time.Second)
}
}
执行结果:
6.runN():
源码位置:src/testing/benchmark.go
// runN runs a single benchmark for the specified number of iterations.
func (b *B) runN(n int) {
benchmarkLock.Lock()
defer benchmarkLock.Unlock()
ctx, cancelCtx := context.WithCancel(context.Background())
defer func() {
b.runCleanup(normalPanic)
b.checkRaces()
}()
// Try to get a comparable environment for each run
// by clearing garbage from previous runs.
runtime.GC()
b.resetRaces()
b.N = n
b.loopN = 0
b.ctx = ctx
b.cancelCtx = cancelCtx
b.parallelism = 1
b.ResetTimer()
b.StartTimer()
b.benchFunc(b)
b.StopTimer()
b.previousN = n
b.previousDuration = b.duration
}
流程图:
7.内存统计:
源码位置:src/testing/benchmark.go
// BenchmarkResult contains the results of a benchmark run.
type BenchmarkResult struct {
N int // The number of iterations.
T time.Duration // The total time taken.
Bytes int64 // Bytes processed in one iteration.
MemAllocs uint64 // The total number of memory allocations.
MemBytes uint64 // The total number of bytes allocated.
// Extra records additional metrics reported by ReportMetric.
Extra map[string]float64
}
N:最终执行的迭代次数.
T:总耗时.
Bytes:单词迭代处理的字节数.
MemAllocs:总内存分配次数.
MemBytes:总内存分配字节数.
Extra:自定义性能指标.
生成流程(关联runN):
AllocsPerOp()函数:
// AllocsPerOp returns the "allocs/op" metric,
// which is calculated as r.MemAllocs / r.N.
func (r BenchmarkResult) AllocsPerOp() int64 {
if v, ok := r.Extra["allocs/op"]; ok {
return int64(v)
}
if r.N <= 0 {
return 0
}
return int64(r.MemAllocs) / int64(r.N)
}
流程图:
AllocedBytesPerOp()函数:
// AllocedBytesPerOp returns the "B/op" metric,
// which is calculated as r.MemBytes / r.N.
func (r BenchmarkResult) AllocedBytesPerOp() int64 {
if v, ok := r.Extra["B/op"]; ok {
return int64(v)
}
if r.N <= 0 {
return 0
}
return int64(r.MemBytes) / int64(r.N)
}
流程图:
******
语雀地址www.yuque.com/itbosunmian…?
《Go.》 密码:xbkk 欢迎大家访问.提意见.
岁月悠悠.
如果大家喜欢我的分享的话.可以关注我的微信公众号
念何架构之路