1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-09-16 09:26:25 +02:00

Refactor the metric SDK benchmarks (#3992)

Benchmark all instruments, not just an int64 counter.

Include benchmarks for all synchronous measurement methods.

Include benchmarks for all collections.
This commit is contained in:
Tyler Yahn
2023-04-14 07:51:10 -07:00
committed by GitHub
parent cf8367f711
commit 4b5abe06d2

View File

@@ -16,134 +16,353 @@ package metric // import "go.opentelemetry.io/otel/sdk/metric"
import (
"context"
"fmt"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/sdk/metric/aggregation"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
)
func benchCounter(b *testing.B, views ...View) (context.Context, Reader, instrument.Int64Counter) {
var viewBenchmarks = []struct {
Name string
Views []View
}{
{"NoView", []View{}},
{
"DropView",
[]View{NewView(
Instrument{Name: "*"},
Stream{Aggregation: aggregation.Drop{}},
)},
},
{
"AttrFilterView",
[]View{NewView(
Instrument{Name: "*"},
Stream{AttributeFilter: func(kv attribute.KeyValue) bool {
return kv.Key == attribute.Key("K")
}},
)},
},
}
func BenchmarkSyncMeasure(b *testing.B) {
for _, bc := range viewBenchmarks {
b.Run(bc.Name, benchSyncViews(bc.Views...))
}
}
func benchSyncViews(views ...View) func(*testing.B) {
ctx := context.Background()
rdr := NewManualReader()
provider := NewMeterProvider(WithReader(rdr), WithView(views...))
cntr, _ := provider.Meter("test").Int64Counter("hello")
b.ResetTimer()
b.ReportAllocs()
return ctx, rdr, cntr
}
func BenchmarkCounterAddNoAttrs(b *testing.B) {
ctx, _, cntr := benchCounter(b)
for i := 0; i < b.N; i++ {
cntr.Add(ctx, 1)
}
}
func BenchmarkCounterAddOneAttr(b *testing.B) {
ctx, _, cntr := benchCounter(b)
for i := 0; i < b.N; i++ {
cntr.Add(ctx, 1, attribute.String("K", "V"))
}
}
func BenchmarkCounterAddOneInvalidAttr(b *testing.B) {
ctx, _, cntr := benchCounter(b)
for i := 0; i < b.N; i++ {
cntr.Add(ctx, 1, attribute.String("", "V"), attribute.String("K", "V"))
}
}
func BenchmarkCounterAddSingleUseAttrs(b *testing.B) {
ctx, _, cntr := benchCounter(b)
for i := 0; i < b.N; i++ {
cntr.Add(ctx, 1, attribute.Int("K", i))
}
}
func BenchmarkCounterAddSingleUseInvalidAttrs(b *testing.B) {
ctx, _, cntr := benchCounter(b)
for i := 0; i < b.N; i++ {
cntr.Add(ctx, 1, attribute.Int("", i), attribute.Int("K", i))
}
}
func BenchmarkCounterAddSingleUseFilteredAttrs(b *testing.B) {
ctx, _, cntr := benchCounter(b, NewView(
Instrument{Name: "*"},
Stream{AttributeFilter: func(kv attribute.KeyValue) bool {
return kv.Key == attribute.Key("K")
}},
))
for i := 0; i < b.N; i++ {
cntr.Add(ctx, 1, attribute.Int("L", i), attribute.Int("K", i))
}
}
func BenchmarkCounterCollectOneAttr(b *testing.B) {
ctx, rdr, cntr := benchCounter(b)
for i := 0; i < b.N; i++ {
cntr.Add(ctx, 1, attribute.Int("K", 1))
_ = rdr.Collect(ctx, nil)
}
}
func BenchmarkCounterCollectTenAttrs(b *testing.B) {
ctx, rdr, cntr := benchCounter(b)
for i := 0; i < b.N; i++ {
for j := 0; j < 10; j++ {
cntr.Add(ctx, 1, attribute.Int("K", j))
}
_ = rdr.Collect(ctx, nil)
}
}
func BenchmarkCollectHistograms(b *testing.B) {
b.Run("1", benchCollectHistograms(1))
b.Run("5", benchCollectHistograms(5))
b.Run("10", benchCollectHistograms(10))
b.Run("25", benchCollectHistograms(25))
}
func benchCollectHistograms(count int) func(*testing.B) {
ctx := context.Background()
r := NewManualReader()
mtr := NewMeterProvider(
WithReader(r),
).Meter("sdk/metric/bench/histogram")
for i := 0; i < count; i++ {
name := fmt.Sprintf("fake data %d", i)
h, _ := mtr.Int64Histogram(name)
h.Record(ctx, 1)
}
// Store benchmark results in a closure to prevent the compiler from
// inlining and skipping the function.
var (
collectedMetrics metricdata.ResourceMetrics
)
meter := provider.Meter("benchSyncViews")
return func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
iCtr, err := meter.Int64Counter("int64-counter")
assert.NoError(b, err)
b.Run("Int64Counter", benchMeasAttrs(func() measF {
return func(attr []attribute.KeyValue) func() {
return func() { iCtr.Add(ctx, 1, attr...) }
}
}()))
for n := 0; n < b.N; n++ {
_ = r.Collect(ctx, &collectedMetrics)
if len(collectedMetrics.ScopeMetrics[0].Metrics) != count {
b.Fatalf("got %d metrics, want %d", len(collectedMetrics.ScopeMetrics[0].Metrics), count)
fCtr, err := meter.Float64Counter("float64-counter")
assert.NoError(b, err)
b.Run("Float64Counter", benchMeasAttrs(func() measF {
return func(attr []attribute.KeyValue) func() {
return func() { fCtr.Add(ctx, 1, attr...) }
}
}()))
iUDCtr, err := meter.Int64UpDownCounter("int64-up-down-counter")
assert.NoError(b, err)
b.Run("Int64UpDownCounter", benchMeasAttrs(func() measF {
return func(attr []attribute.KeyValue) func() {
return func() { iUDCtr.Add(ctx, 1, attr...) }
}
}()))
fUDCtr, err := meter.Float64UpDownCounter("float64-up-down-counter")
assert.NoError(b, err)
b.Run("Float64UpDownCounter", benchMeasAttrs(func() measF {
return func(attr []attribute.KeyValue) func() {
return func() { fUDCtr.Add(ctx, 1, attr...) }
}
}()))
iHist, err := meter.Int64Histogram("int64-histogram")
assert.NoError(b, err)
b.Run("Int64Histogram", benchMeasAttrs(func() measF {
return func(attr []attribute.KeyValue) func() {
return func() { iHist.Record(ctx, 1, attr...) }
}
}()))
fHist, err := meter.Float64Histogram("float64-histogram")
assert.NoError(b, err)
b.Run("Float64Histogram", benchMeasAttrs(func() measF {
return func(attr []attribute.KeyValue) func() {
return func() { fHist.Record(ctx, 1, attr...) }
}
}()))
}
}
type measF func(attr []attribute.KeyValue) func()
func benchMeasAttrs(meas measF) func(*testing.B) {
return func(b *testing.B) {
b.Run("Attributes/0", func(b *testing.B) {
f := meas(nil)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
f()
}
})
b.Run("Attributes/1", func(b *testing.B) {
attrs := []attribute.KeyValue{attribute.Bool("K", true)}
f := meas(attrs)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
f()
}
})
b.Run("Attributes/10", func(b *testing.B) {
n := 10
attrs := make([]attribute.KeyValue, 0)
attrs = append(attrs, attribute.Bool("K", true))
for i := 2; i < n; i++ {
attrs = append(attrs, attribute.Int(strconv.Itoa(i), i))
}
f := meas(attrs)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
f()
}
})
}
}
func BenchmarkCollect(b *testing.B) {
for _, bc := range viewBenchmarks {
b.Run(bc.Name, benchCollectViews(bc.Views...))
}
}
func benchCollectViews(views ...View) func(*testing.B) {
setup := func(name string) (metric.Meter, Reader) {
r := NewManualReader()
mp := NewMeterProvider(WithReader(r), WithView(views...))
return mp.Meter(name), r
}
ctx := context.Background()
return func(b *testing.B) {
b.Run("Int64Counter/1", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Int64Counter")
i, err := m.Int64Counter("int64-counter")
assert.NoError(b, err)
i.Add(ctx, 1, attr...)
return r
}))
b.Run("Int64Counter/10", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Int64Counter")
i, err := m.Int64Counter("int64-counter")
assert.NoError(b, err)
for n := 0; n < 10; n++ {
i.Add(ctx, 1, attr...)
}
return r
}))
b.Run("Float64Counter/1", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Float64Counter")
i, err := m.Float64Counter("float64-counter")
assert.NoError(b, err)
i.Add(ctx, 1, attr...)
return r
}))
b.Run("Float64Counter/10", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Float64Counter")
i, err := m.Float64Counter("float64-counter")
assert.NoError(b, err)
for n := 0; n < 10; n++ {
i.Add(ctx, 1, attr...)
}
return r
}))
b.Run("Int64UpDownCounter/1", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Int64UpDownCounter")
i, err := m.Int64UpDownCounter("int64-up-down-counter")
assert.NoError(b, err)
i.Add(ctx, 1, attr...)
return r
}))
b.Run("Int64UpDownCounter/10", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Int64UpDownCounter")
i, err := m.Int64UpDownCounter("int64-up-down-counter")
assert.NoError(b, err)
for n := 0; n < 10; n++ {
i.Add(ctx, 1, attr...)
}
return r
}))
b.Run("Float64UpDownCounter/1", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Float64UpDownCounter")
i, err := m.Float64UpDownCounter("float64-up-down-counter")
assert.NoError(b, err)
i.Add(ctx, 1, attr...)
return r
}))
b.Run("Float64UpDownCounter/10", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Float64UpDownCounter")
i, err := m.Float64UpDownCounter("float64-up-down-counter")
assert.NoError(b, err)
for n := 0; n < 10; n++ {
i.Add(ctx, 1, attr...)
}
return r
}))
b.Run("Int64Histogram/1", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Int64Histogram")
i, err := m.Int64Histogram("int64-histogram")
assert.NoError(b, err)
i.Record(ctx, 1, attr...)
return r
}))
b.Run("Int64Histogram/10", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Int64Histogram")
i, err := m.Int64Histogram("int64-histogram")
assert.NoError(b, err)
for n := 0; n < 10; n++ {
i.Record(ctx, 1, attr...)
}
return r
}))
b.Run("Float64Histogram/1", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Float64Histogram")
i, err := m.Float64Histogram("float64-histogram")
assert.NoError(b, err)
i.Record(ctx, 1, attr...)
return r
}))
b.Run("Float64Histogram/10", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Float64Histogram")
i, err := m.Float64Histogram("float64-histogram")
assert.NoError(b, err)
for n := 0; n < 10; n++ {
i.Record(ctx, 1, attr...)
}
return r
}))
b.Run("Int64ObservableCounter", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Int64ObservableCounter")
_, err := m.Int64ObservableCounter(
"int64-observable-counter",
instrument.WithInt64Callback(int64Cback(attr)),
)
assert.NoError(b, err)
return r
}))
b.Run("Float64ObservableCounter", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Float64ObservableCounter")
_, err := m.Float64ObservableCounter(
"float64-observable-counter",
instrument.WithFloat64Callback(float64Cback(attr)),
)
assert.NoError(b, err)
return r
}))
b.Run("Int64ObservableUpDownCounter", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Int64ObservableUpDownCounter")
_, err := m.Int64ObservableUpDownCounter(
"int64-observable-up-down-counter",
instrument.WithInt64Callback(int64Cback(attr)),
)
assert.NoError(b, err)
return r
}))
b.Run("Float64ObservableUpDownCounter", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Float64ObservableUpDownCounter")
_, err := m.Float64ObservableUpDownCounter(
"float64-observable-up-down-counter",
instrument.WithFloat64Callback(float64Cback(attr)),
)
assert.NoError(b, err)
return r
}))
b.Run("Int64ObservableGauge", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Int64ObservableGauge")
_, err := m.Int64ObservableGauge(
"int64-observable-gauge",
instrument.WithInt64Callback(int64Cback(attr)),
)
assert.NoError(b, err)
return r
}))
b.Run("Float64ObservableGauge", benchCollectAttrs(func(attr []attribute.KeyValue) Reader {
m, r := setup("benchCollectViews/Float64ObservableGauge")
_, err := m.Float64ObservableGauge(
"float64-observable-gauge",
instrument.WithFloat64Callback(float64Cback(attr)),
)
assert.NoError(b, err)
return r
}))
}
}
func int64Cback(attr []attribute.KeyValue) instrument.Int64Callback {
return func(_ context.Context, o instrument.Int64Observer) error {
o.Observe(1, attr...)
return nil
}
}
func float64Cback(attr []attribute.KeyValue) instrument.Float64Callback {
return func(_ context.Context, o instrument.Float64Observer) error {
o.Observe(1, attr...)
return nil
}
}
func benchCollectAttrs(setup func([]attribute.KeyValue) Reader) func(*testing.B) {
ctx := context.Background()
out := new(metricdata.ResourceMetrics)
run := func(reader Reader) func(b *testing.B) {
return func(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
_ = reader.Collect(ctx, out)
}
}
}
return func(b *testing.B) {
b.Run("Attributes/0", run(setup(nil)))
attrs := []attribute.KeyValue{attribute.Bool("K", true)}
b.Run("Attributes/1", run(setup(attrs)))
for i := 2; i < 10; i++ {
attrs = append(attrs, attribute.Int(strconv.Itoa(i), i))
}
b.Run("Attributes/10", run(setup(attrs)))
}
}