From: Austin Clements Date: Fri, 27 Oct 2017 19:29:05 +0000 (-0400) Subject: runtime: benchmark for bulk write barriers X-Git-Tag: go1.10beta1~537 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=f96b95bcd192c771d815d2699e3aae9bbb65f6b2;p=gostls13.git runtime: benchmark for bulk write barriers This adds a benchmark of typedslicecopy and its bulk write barriers. For #22460. Change-Id: I439ca3b130bb22944468095f8f18b464e5bb43ca Reviewed-on: https://go-review.googlesource.com/74051 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Rick Hudson --- diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go index a5c4c3bb04..1b1db25b17 100644 --- a/src/runtime/gc_test.go +++ b/src/runtime/gc_test.go @@ -517,6 +517,32 @@ func TestUserForcedGC(t *testing.T) { } } +func writeBarrierBenchmark(b *testing.B, f func()) { + runtime.GC() + var ms runtime.MemStats + runtime.ReadMemStats(&ms) + //b.Logf("heap size: %d MB", ms.HeapAlloc>>20) + + // Keep GC running continuously during the benchmark, which in + // turn keeps the write barrier on continuously. + var stop uint32 + done := make(chan bool) + go func() { + for atomic.LoadUint32(&stop) == 0 { + runtime.GC() + } + close(done) + }() + defer func() { + atomic.StoreUint32(&stop, 1) + <-done + }() + + b.ResetTimer() + f() + b.StopTimer() +} + func BenchmarkWriteBarrier(b *testing.B) { if runtime.GOMAXPROCS(-1) < 2 { // We don't want GC to take our time. @@ -546,53 +572,70 @@ func BenchmarkWriteBarrier(b *testing.B) { const depth = 22 // 64 MB root := mkTree(22) - runtime.GC() - var ms runtime.MemStats - runtime.ReadMemStats(&ms) - //b.Logf("heap size: %d MB", ms.HeapAlloc>>20) + writeBarrierBenchmark(b, func() { + var stack [depth]*node + tos := -1 - // Keep GC running continuously during the benchmark. - var stop uint32 - done := make(chan bool) - go func() { - for atomic.LoadUint32(&stop) == 0 { - runtime.GC() + // There are two write barriers per iteration, so i+=2. + for i := 0; i < b.N; i += 2 { + if tos == -1 { + stack[0] = root + tos = 0 + } + + // Perform one step of reversing the tree. + n := stack[tos] + if n.l == nil { + tos-- + } else { + n.l, n.r = n.r, n.l + stack[tos] = n.l + stack[tos+1] = n.r + tos++ + } + + if i%(1<<12) == 0 { + // Avoid non-preemptible loops (see issue #10958). + runtime.Gosched() + } } - close(done) - }() + }) - b.ResetTimer() + runtime.KeepAlive(wbRoots) +} - var stack [depth]*node - tos := -1 +func BenchmarkBulkWriteBarrier(b *testing.B) { + if runtime.GOMAXPROCS(-1) < 2 { + // We don't want GC to take our time. + b.Skip("need GOMAXPROCS >= 2") + } - // There are two write barriers per iteration, so i+=2. - for i := 0; i < b.N; i += 2 { - if tos == -1 { - stack[0] = root - tos = 0 - } + // Construct a large set of objects we can copy around. + const heapSize = 64 << 20 + type obj [16]*byte + ptrs := make([]*obj, heapSize/unsafe.Sizeof(obj{})) + for i := range ptrs { + ptrs[i] = new(obj) + } - // Perform one step of reversing the tree. - n := stack[tos] - if n.l == nil { - tos-- - } else { - n.l, n.r = n.r, n.l - stack[tos] = n.l - stack[tos+1] = n.r - tos++ - } + writeBarrierBenchmark(b, func() { + const blockSize = 1024 + var pos int + for i := 0; i < b.N; i += blockSize { + // Rotate block. + block := ptrs[pos : pos+blockSize] + first := block[0] + copy(block, block[1:]) + block[blockSize-1] = first + + pos += blockSize + if pos+blockSize > len(ptrs) { + pos = 0 + } - if i%(1<<12) == 0 { - // Avoid non-preemptible loops (see issue #10958). runtime.Gosched() } - } + }) - b.StopTimer() - atomic.StoreUint32(&stop, 1) - <-done - - runtime.KeepAlive(wbRoots) + runtime.KeepAlive(ptrs) }