上卷 程序设计
中卷 标准库
- 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.1 并行
方法 b.RunParallel
创建多个 goroutine 并发测试单个目标。
- 不能操作计时器。(
StartTimer
、StopTimer
、ResetTimer
) - 不能执行子测试。(
Run
)
func BenchmarkA(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { time.Sleep(time.Microsecond) } }) }
$ go test -v -bench . -cpu 1,2,4 === RUN TestA --- PASS: TestA (0.00s) === RUN TestA --- PASS: TestA (0.00s) === RUN TestA --- PASS: TestA (0.00s) BenchmarkA BenchmarkA 54733 21102 ns/op BenchmarkA-2 112624 10242 ns/op BenchmarkA-4 108907 10186 ns/op PASS ok test 3.910s
内部实现
将总次数( b.N
, bN
)按粒度( grain
, ~100µs
)分成多个段。
每个 goroutine 每次取一个分段执行。如总任务未完成,则再取一个分段。
使用计数器( cache
)记录当前分段任务内部进度。每次调用 PB.Next
,计数器递减。
归零时,将此次分段计入总进度( globalN
)。检查总进度,判断是否要再取分段。
|-------------|--------------|-------------|-----//------| 0 g1 g2 g1 g2 bN
type PB struct { globalN *uint64 // 总进度(多个 G 进度相加)。 grain uint64 // 分段粒度。 cache uint64 // 当前分段进度。 bN uint64 // 任务总次数(b.N) }
// benchmark.go func (b *B) RunParallel(body func(*PB)) { // 计算分段粒度(~100µs)。 grain := uint64(0) if b.previousN > 0 && b.previousDuration > 0 { grain = 1e5 * uint64(b.previousN) / uint64(b.previousDuration) } n := uint64(0) numProcs := b.parallelism * runtime.GOMAXPROCS(0) var wg sync.WaitGroup wg.Add(numProcs) // 启动多个 G 并发执行任务。 for p := 0; p < numProcs; p++ { go func() { defer wg.Done() pb := &PB{ globalN: &n, grain: grain, bN: uint64(b.N), } body(pb) }() } // 等待。 wg.Wait() }
func (pb *PB) Next() bool { // 刚开始,或当前分段(pb.cache)结束。 if pb.cache == 0 { // 累加总进度(pb.globalN)。 n := atomic.AddUint64(pb.globalN, pb.grain) // 如果总任务(pb.bN)未结束,则获取下一分段。 if n <= pb.bN { pb.cache = pb.grain } else if n < pb.bN+pb.grain { pb.cache = pb.bN + pb.grain - n } else { return false } } // 每次执行,递减当前分段计数。 pb.cache-- return true }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论