diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go index a5c4c3bb04b..1b1db25b175 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() - - var stack [depth]*node - tos := -1 - - // 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() - } - } - - b.StopTimer() - atomic.StoreUint32(&stop, 1) - <-done + }) runtime.KeepAlive(wbRoots) } + +func BenchmarkBulkWriteBarrier(b *testing.B) { + if runtime.GOMAXPROCS(-1) < 2 { + // We don't want GC to take our time. + b.Skip("need GOMAXPROCS >= 2") + } + + // 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) + } + + 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 + } + + runtime.Gosched() + } + }) + + runtime.KeepAlive(ptrs) +}