mirror of
https://gitlab.com/pulsechaincom/go-pulse.git
synced 2024-12-22 11:31:02 +00:00
c539bda166
This changes how we read performance metrics from the Go runtime. Instead of using runtime.ReadMemStats, we now rely on the API provided by package runtime/metrics. runtime/metrics provides more accurate information. For example, the new interface has better reporting of memory use. In my testing, the reported value of held memory more accurately reflects the usage reported by the OS. The semantics of metrics system/memory/allocs and system/memory/frees have changed to report amounts in bytes. ReadMemStats only reported the count of allocations in number-of-objects. This is imprecise: 'tiny objects' are not counted because the runtime allocates them in batches; and certain improvements in allocation behavior, such as struct size optimizations, will be less visible when the number of allocs doesn't change. Changing allocation reports to be in bytes makes it appear in graphs that lots more is being allocated. I don't think that's a problem because this metric is primarily interesting for geth developers. The metric system/memory/pauses has been changed to report statistical values from the histogram provided by the runtime. Its name in influxdb has changed from geth.system/memory/pauses.meter to geth.system/memory/pauses.histogram. We also have a new histogram metric, system/cpu/schedlatency, reporting the Go scheduler latency.
134 lines
3.3 KiB
Go
134 lines
3.3 KiB
Go
package metrics
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"runtime/metrics"
|
|
"testing"
|
|
)
|
|
|
|
var _ Histogram = (*runtimeHistogram)(nil)
|
|
|
|
type runtimeHistogramTest struct {
|
|
h metrics.Float64Histogram
|
|
|
|
Count int64
|
|
Min int64
|
|
Max int64
|
|
Sum int64
|
|
Mean float64
|
|
Variance float64
|
|
StdDev float64
|
|
Percentiles []float64 // .5 .8 .9 .99 .995
|
|
}
|
|
|
|
// This test checks the results of statistical functions implemented
|
|
// by runtimeHistogramSnapshot.
|
|
func TestRuntimeHistogramStats(t *testing.T) {
|
|
tests := []runtimeHistogramTest{
|
|
0: {
|
|
h: metrics.Float64Histogram{
|
|
Counts: []uint64{},
|
|
Buckets: []float64{},
|
|
},
|
|
Count: 0,
|
|
Max: 0,
|
|
Min: 0,
|
|
Sum: 0,
|
|
Mean: 0,
|
|
Variance: 0,
|
|
StdDev: 0,
|
|
Percentiles: []float64{0, 0, 0, 0, 0},
|
|
},
|
|
1: {
|
|
// This checks the case where the highest bucket is +Inf.
|
|
h: metrics.Float64Histogram{
|
|
Counts: []uint64{0, 1, 2},
|
|
Buckets: []float64{0, 0.5, 1, math.Inf(1)},
|
|
},
|
|
Count: 3,
|
|
Max: 1,
|
|
Min: 0,
|
|
Sum: 3,
|
|
Mean: 0.9166666,
|
|
Percentiles: []float64{1, 1, 1, 1, 1},
|
|
Variance: 0.020833,
|
|
StdDev: 0.144433,
|
|
},
|
|
2: {
|
|
h: metrics.Float64Histogram{
|
|
Counts: []uint64{8, 6, 3, 1},
|
|
Buckets: []float64{12, 16, 18, 24, 25},
|
|
},
|
|
Count: 18,
|
|
Max: 25,
|
|
Min: 12,
|
|
Sum: 270,
|
|
Mean: 16.75,
|
|
Variance: 10.3015,
|
|
StdDev: 3.2096,
|
|
Percentiles: []float64{16, 18, 18, 24, 24},
|
|
},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
|
s := runtimeHistogramSnapshot(test.h)
|
|
|
|
if v := s.Count(); v != test.Count {
|
|
t.Errorf("Count() = %v, want %v", v, test.Count)
|
|
}
|
|
if v := s.Min(); v != test.Min {
|
|
t.Errorf("Min() = %v, want %v", v, test.Min)
|
|
}
|
|
if v := s.Max(); v != test.Max {
|
|
t.Errorf("Max() = %v, want %v", v, test.Max)
|
|
}
|
|
if v := s.Sum(); v != test.Sum {
|
|
t.Errorf("Sum() = %v, want %v", v, test.Sum)
|
|
}
|
|
if v := s.Mean(); !approxEqual(v, test.Mean, 0.0001) {
|
|
t.Errorf("Mean() = %v, want %v", v, test.Mean)
|
|
}
|
|
if v := s.Variance(); !approxEqual(v, test.Variance, 0.0001) {
|
|
t.Errorf("Variance() = %v, want %v", v, test.Variance)
|
|
}
|
|
if v := s.StdDev(); !approxEqual(v, test.StdDev, 0.0001) {
|
|
t.Errorf("StdDev() = %v, want %v", v, test.StdDev)
|
|
}
|
|
ps := []float64{.5, .8, .9, .99, .995}
|
|
if v := s.Percentiles(ps); !reflect.DeepEqual(v, test.Percentiles) {
|
|
t.Errorf("Percentiles(%v) = %v, want %v", ps, v, test.Percentiles)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func approxEqual(x, y, ε float64) bool {
|
|
if math.IsInf(x, -1) && math.IsInf(y, -1) {
|
|
return true
|
|
}
|
|
if math.IsInf(x, 1) && math.IsInf(y, 1) {
|
|
return true
|
|
}
|
|
if math.IsNaN(x) && math.IsNaN(y) {
|
|
return true
|
|
}
|
|
return math.Abs(x-y) < ε
|
|
}
|
|
|
|
// This test verifies that requesting Percentiles in unsorted order
|
|
// returns them in the requested order.
|
|
func TestRuntimeHistogramStatsPercentileOrder(t *testing.T) {
|
|
p := runtimeHistogramSnapshot{
|
|
Counts: []uint64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
|
Buckets: []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
|
}
|
|
result := p.Percentiles([]float64{1, 0.2, 0.5, 0.1, 0.2})
|
|
expected := []float64{10, 2, 5, 1, 2}
|
|
if !reflect.DeepEqual(result, expected) {
|
|
t.Fatal("wrong result:", result)
|
|
}
|
|
}
|