"reflect"
"runtime"
"runtime/debug"
+ "sync/atomic"
"testing"
"time"
"unsafe"
t.Fatalf("runtime.GC() was not accounted in NumForcedGC")
}
}
+
+func BenchmarkWriteBarrier(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 tree both so the GC runs for a while and
+ // so we have a data structure to manipulate the pointers of.
+ type node struct {
+ l, r *node
+ }
+ var wbRoots []*node
+ var mkTree func(level int) *node
+ mkTree = func(level int) *node {
+ if level == 0 {
+ return nil
+ }
+ n := &node{mkTree(level - 1), mkTree(level - 1)}
+ if level == 10 {
+ // Seed GC with enough early pointers so it
+ // doesn't accidentally switch to mark 2 when
+ // it only has the top of the tree.
+ wbRoots = append(wbRoots, n)
+ }
+ return n
+ }
+ 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)
+
+ // Keep GC running continuously during the benchmark.
+ var stop uint32
+ done := make(chan bool)
+ go func() {
+ for atomic.LoadUint32(&stop) == 0 {
+ runtime.GC()
+ }
+ 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)
+}