Procházet zdrojové kódy

Research, benchmarks, and tests.

Richard Crowley před 12 roky
rodič
revize
a9f31ff475
5 změnil soubory, kde provedl 90 přidání a 7 odebrání
  1. 10 0
      debug.go
  2. 24 1
      debug_test.go
  3. 28 5
      metrics_test.go
  4. 4 0
      runtime.go
  5. 24 1
      runtime_test.go

+ 10 - 0
debug.go

@@ -32,6 +32,10 @@ func CaptureDebugGCStats(r Registry, d time.Duration) {
 // debug.GCStats.  This is designed to be called in a background goroutine.
 // Giving a registry which has not been given to RegisterDebugGCStats will
 // panic.
+//
+// Be careful (but much less so) with this because debug.ReadGCStats calls
+// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world
+// operation, isn't something you want to be doing all the time.
 func CaptureDebugGCStatsOnce(r Registry) {
 	lastGC := gcStats.LastGC
 	t := time.Now()
@@ -65,3 +69,9 @@ func RegisterDebugGCStats(r Registry) {
 	r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal)
 	r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats)
 }
+
+// Allocate an initial slice for gcStats.Pause to avoid allocations during
+// normal operation.
+func init() {
+	gcStats.Pause = make([]time.Duration, 11)
+}

+ 24 - 1
debug_test.go

@@ -1,6 +1,10 @@
 package metrics
 
-import "testing"
+import (
+	"runtime/debug"
+	"testing"
+	"time"
+)
 
 func BenchmarkDebugGCStats(b *testing.B) {
 	r := NewRegistry()
@@ -10,3 +14,22 @@ func BenchmarkDebugGCStats(b *testing.B) {
 		CaptureDebugGCStatsOnce(r)
 	}
 }
+
+func TestDebugGCStatsBlocking(t *testing.T) {
+	ch := make(chan int)
+	go func() {
+		i := 0
+		for {
+			select {
+			case ch <- i:
+				return
+			default:
+				i++
+			}
+			time.Sleep(1e3)
+		}
+	}()
+	var gcStats debug.GCStats
+	debug.ReadGCStats(&gcStats)
+	t.Log(<-ch)
+}

+ 28 - 5
metrics_test.go

@@ -7,7 +7,7 @@ import (
 	"testing"
 )
 
-const FANOUT = 4
+const FANOUT = 128
 
 // Stop the compiler from complaining during debugging.
 var (
@@ -22,14 +22,34 @@ func BenchmarkMetrics(b *testing.B) {
 	h := NewRegisteredHistogram("histogram", r, NewUniformSample(100))
 	m := NewRegisteredMeter("meter", r)
 	t := NewRegisteredTimer("timer", r)
+	RegisterDebugGCStats(r)
 	RegisterRuntimeMemStats(r)
 	b.ResetTimer()
 	ch := make(chan bool)
-	wgC := &sync.WaitGroup{}
+
+	wgD := &sync.WaitGroup{}
+/*
+	wgD.Add(1)
+	go func() {
+		defer wgD.Done()
+		//log.Println("go CaptureDebugGCStats")
+		for {
+			select {
+			case <-ch:
+				//log.Println("done CaptureDebugGCStats")
+				return
+			default:
+				CaptureDebugGCStatsOnce(r)
+			}
+		}
+	}()
+//*/
+
+	wgR := &sync.WaitGroup{}
 //*
-	wgC.Add(1)
+	wgR.Add(1)
 	go func() {
-		defer wgC.Done()
+		defer wgR.Done()
 		//log.Println("go CaptureRuntimeMemStats")
 		for {
 			select {
@@ -42,6 +62,7 @@ func BenchmarkMetrics(b *testing.B) {
 		}
 	}()
 //*/
+
 	wgW := &sync.WaitGroup{}
 /*
 	wgW.Add(1)
@@ -59,6 +80,7 @@ func BenchmarkMetrics(b *testing.B) {
 		}
 	}()
 //*/
+
 	wg := &sync.WaitGroup{}
 	wg.Add(FANOUT)
 	for i := 0; i < FANOUT; i++ {
@@ -77,6 +99,7 @@ func BenchmarkMetrics(b *testing.B) {
 	}
 	wg.Wait()
 	close(ch)
-	wgC.Wait()
+	wgD.Wait()
+	wgR.Wait()
 	wgW.Wait()
 }

+ 4 - 0
runtime.go

@@ -56,6 +56,10 @@ func CaptureRuntimeMemStats(r Registry, d time.Duration) {
 // runtime.MemStats.  This is designed to be called in a background
 // goroutine.  Giving a registry which has not been given to
 // RegisterRuntimeMemStats will panic.
+//
+// Be very careful with this because runtime.ReadMemStats calls the C
+// functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld()
+// and that last one does what it says on the tin.
 func CaptureRuntimeMemStatsOnce(r Registry) {
 	t := time.Now()
 	runtime.ReadMemStats(&memStats) // This takes 50-200us.

+ 24 - 1
runtime_test.go

@@ -1,6 +1,10 @@
 package metrics
 
-import "testing"
+import (
+	"runtime"
+	"testing"
+	"time"
+)
 
 func BenchmarkRuntimeMemStats(b *testing.B) {
 	r := NewRegistry()
@@ -10,3 +14,22 @@ func BenchmarkRuntimeMemStats(b *testing.B) {
 		CaptureRuntimeMemStatsOnce(r)
 	}
 }
+
+func TestRuntimeMemStatsBlocking(t *testing.T) {
+	ch := make(chan int)
+	go func() {
+		i := 0
+		for {
+			select {
+			case ch <- i:
+				return
+			default:
+				i++
+			}
+			time.Sleep(1e3)
+		}
+	}()
+	var memStats runtime.MemStats
+	runtime.ReadMemStats(&memStats)
+	t.Log(<-ch)
+}