runtime: add readMetrics latency benchmark

This change adds a new benchmark to the runtime tests for measuring the
latency of the new metrics implementation, based on the
ReadMemStats latency benchmark. readMetrics will have more metrics added
to it in the future, and this benchmark will serve as a way to measure
the cost of adding additional metrics.

Change-Id: Ib05e3ed4afa49a70863fc0c418eab35b72263e24
Reviewed-on: https://go-review.googlesource.com/c/go/+/247042
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
Michael Anthony Knyszek 2020-08-05 23:10:46 +00:00 committed by Michael Knyszek
parent b08dfbaa43
commit 74e566ed1d
2 changed files with 50 additions and 5 deletions

View file

@ -518,7 +518,7 @@ func BenchmarkReadMemStats(b *testing.B) {
hugeSink = nil hugeSink = nil
} }
func BenchmarkReadMemStatsLatency(b *testing.B) { func applyGCLoad(b *testing.B) func() {
// Well apply load to the runtime with maxProcs-1 goroutines // Well apply load to the runtime with maxProcs-1 goroutines
// and use one more to actually benchmark. It doesn't make sense // and use one more to actually benchmark. It doesn't make sense
// to try to run this test with only 1 P (that's what // to try to run this test with only 1 P (that's what
@ -563,6 +563,14 @@ func BenchmarkReadMemStatsLatency(b *testing.B) {
runtime.KeepAlive(hold) runtime.KeepAlive(hold)
}() }()
} }
return func() {
close(done)
wg.Wait()
}
}
func BenchmarkReadMemStatsLatency(b *testing.B) {
stop := applyGCLoad(b)
// Spend this much time measuring latencies. // Spend this much time measuring latencies.
latencies := make([]time.Duration, 0, 1024) latencies := make([]time.Duration, 0, 1024)
@ -579,12 +587,11 @@ func BenchmarkReadMemStatsLatency(b *testing.B) {
runtime.ReadMemStats(&ms) runtime.ReadMemStats(&ms)
latencies = append(latencies, time.Now().Sub(start)) latencies = append(latencies, time.Now().Sub(start))
} }
close(done) // Make sure to stop the timer before we wait! The load created above
// Make sure to stop the timer before we wait! The goroutines above // is very heavy-weight and not easy to stop, so we could end up
// are very heavy-weight and not easy to stop, so we could end up
// confusing the benchmarking framework for small b.N. // confusing the benchmarking framework for small b.N.
b.StopTimer() b.StopTimer()
wg.Wait() stop()
// Disable the default */op metrics. // Disable the default */op metrics.
// ns/op doesn't mean anything because it's an average, but we // ns/op doesn't mean anything because it's an average, but we

View file

@ -7,8 +7,10 @@ package runtime_test
import ( import (
"runtime" "runtime"
"runtime/metrics" "runtime/metrics"
"sort"
"strings" "strings"
"testing" "testing"
"time"
"unsafe" "unsafe"
) )
@ -112,3 +114,39 @@ func TestReadMetricsConsistency(t *testing.T) {
t.Errorf(`"/memory/classes/total:bytes" does not match sum of /memory/classes/**: got %d, want %d`, totalVirtual.got, totalVirtual.want) t.Errorf(`"/memory/classes/total:bytes" does not match sum of /memory/classes/**: got %d, want %d`, totalVirtual.got, totalVirtual.want)
} }
} }
func BenchmarkReadMetricsLatency(b *testing.B) {
stop := applyGCLoad(b)
// Spend this much time measuring latencies.
latencies := make([]time.Duration, 0, 1024)
_, samples := prepareAllMetricsSamples()
// Hit metrics.Read continuously and measure.
b.ResetTimer()
for i := 0; i < b.N; i++ {
start := time.Now()
metrics.Read(samples)
latencies = append(latencies, time.Now().Sub(start))
}
// Make sure to stop the timer before we wait! The load created above
// is very heavy-weight and not easy to stop, so we could end up
// confusing the benchmarking framework for small b.N.
b.StopTimer()
stop()
// Disable the default */op metrics.
// ns/op doesn't mean anything because it's an average, but we
// have a sleep in our b.N loop above which skews this significantly.
b.ReportMetric(0, "ns/op")
b.ReportMetric(0, "B/op")
b.ReportMetric(0, "allocs/op")
// Sort latencies then report percentiles.
sort.Slice(latencies, func(i, j int) bool {
return latencies[i] < latencies[j]
})
b.ReportMetric(float64(latencies[len(latencies)*50/100]), "p50-ns")
b.ReportMetric(float64(latencies[len(latencies)*90/100]), "p90-ns")
b.ReportMetric(float64(latencies[len(latencies)*99/100]), "p99-ns")
}