1// Copyright 2019, OpenTelemetry Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package metric_test
16
17import (
18	"context"
19	"fmt"
20	"math/rand"
21	"strings"
22	"testing"
23
24	"go.opentelemetry.io/otel/api/core"
25	"go.opentelemetry.io/otel/api/key"
26	"go.opentelemetry.io/otel/api/metric"
27	export "go.opentelemetry.io/otel/sdk/export/metric"
28	sdk "go.opentelemetry.io/otel/sdk/metric"
29	"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
30	"go.opentelemetry.io/otel/sdk/metric/aggregator/ddsketch"
31	"go.opentelemetry.io/otel/sdk/metric/aggregator/gauge"
32	"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
33)
34
35type benchFixture struct {
36	sdk *sdk.SDK
37	B   *testing.B
38}
39
40func newFixture(b *testing.B) *benchFixture {
41	b.ReportAllocs()
42	bf := &benchFixture{
43		B: b,
44	}
45	bf.sdk = sdk.New(bf, sdk.NewDefaultLabelEncoder())
46	return bf
47}
48
49func (*benchFixture) AggregatorFor(descriptor *export.Descriptor) export.Aggregator {
50	switch descriptor.MetricKind() {
51	case export.CounterKind:
52		return counter.New()
53	case export.GaugeKind:
54		return gauge.New()
55	case export.MeasureKind:
56		if strings.HasSuffix(descriptor.Name(), "minmaxsumcount") {
57			return minmaxsumcount.New(descriptor)
58		} else if strings.HasSuffix(descriptor.Name(), "ddsketch") {
59			return ddsketch.New(ddsketch.NewDefaultConfig(), descriptor)
60		} else if strings.HasSuffix(descriptor.Name(), "array") {
61			return ddsketch.New(ddsketch.NewDefaultConfig(), descriptor)
62		}
63	}
64	return nil
65}
66
67func (*benchFixture) Process(context.Context, export.Record) error {
68	return nil
69}
70
71func (*benchFixture) CheckpointSet() export.CheckpointSet {
72	return nil
73}
74
75func (*benchFixture) FinishedCollection() {
76}
77
78func makeLabelSets(n int) [][]core.KeyValue {
79	r := make([][]core.KeyValue, n)
80
81	for i := 0; i < n; i++ {
82		r[i] = makeLabels(1)
83	}
84
85	return r
86}
87
88func makeLabels(n int) []core.KeyValue {
89	used := map[string]bool{}
90	l := make([]core.KeyValue, n)
91	for i := 0; i < n; i++ {
92		var k string
93		for {
94			k = fmt.Sprint("k", rand.Intn(1000000000))
95			if !used[k] {
96				used[k] = true
97				break
98			}
99		}
100		l[i] = key.New(k).String(fmt.Sprint("v", rand.Intn(1000000000)))
101	}
102	return l
103}
104
105func benchmarkLabels(b *testing.B, n int) {
106	fix := newFixture(b)
107	labs := makeLabels(n)
108
109	b.ResetTimer()
110
111	for i := 0; i < b.N; i++ {
112		fix.sdk.Labels(labs...)
113	}
114}
115
116func BenchmarkLabels_1(b *testing.B) {
117	benchmarkLabels(b, 1)
118}
119
120func BenchmarkLabels_2(b *testing.B) {
121	benchmarkLabels(b, 2)
122}
123
124func BenchmarkLabels_4(b *testing.B) {
125	benchmarkLabels(b, 4)
126}
127
128func BenchmarkLabels_8(b *testing.B) {
129	benchmarkLabels(b, 8)
130}
131
132func BenchmarkLabels_16(b *testing.B) {
133	benchmarkLabels(b, 16)
134}
135
136// Note: performance does not depend on label set size for the
137// benchmarks below.
138
139func BenchmarkAcquireNewHandle(b *testing.B) {
140	fix := newFixture(b)
141	labelSets := makeLabelSets(b.N)
142	cnt := fix.sdk.NewInt64Counter("int64.counter")
143	labels := make([]metric.LabelSet, b.N)
144
145	for i := 0; i < b.N; i++ {
146		labels[i] = fix.sdk.Labels(labelSets[i]...)
147	}
148
149	b.ResetTimer()
150
151	for i := 0; i < b.N; i++ {
152		cnt.Bind(labels[i])
153	}
154}
155
156func BenchmarkAcquireExistingHandle(b *testing.B) {
157	fix := newFixture(b)
158	labelSets := makeLabelSets(b.N)
159	cnt := fix.sdk.NewInt64Counter("int64.counter")
160	labels := make([]metric.LabelSet, b.N)
161
162	for i := 0; i < b.N; i++ {
163		labels[i] = fix.sdk.Labels(labelSets[i]...)
164		cnt.Bind(labels[i]).Unbind()
165	}
166
167	b.ResetTimer()
168
169	for i := 0; i < b.N; i++ {
170		cnt.Bind(labels[i])
171	}
172}
173
174func BenchmarkAcquireReleaseExistingHandle(b *testing.B) {
175	fix := newFixture(b)
176	labelSets := makeLabelSets(b.N)
177	cnt := fix.sdk.NewInt64Counter("int64.counter")
178	labels := make([]metric.LabelSet, b.N)
179
180	for i := 0; i < b.N; i++ {
181		labels[i] = fix.sdk.Labels(labelSets[i]...)
182		cnt.Bind(labels[i]).Unbind()
183	}
184
185	b.ResetTimer()
186
187	for i := 0; i < b.N; i++ {
188		cnt.Bind(labels[i]).Unbind()
189	}
190}
191
192// Counters
193
194func BenchmarkInt64CounterAdd(b *testing.B) {
195	ctx := context.Background()
196	fix := newFixture(b)
197	labs := fix.sdk.Labels(makeLabels(1)...)
198	cnt := fix.sdk.NewInt64Counter("int64.counter")
199
200	b.ResetTimer()
201
202	for i := 0; i < b.N; i++ {
203		cnt.Add(ctx, 1, labs)
204	}
205}
206
207func BenchmarkInt64CounterHandleAdd(b *testing.B) {
208	ctx := context.Background()
209	fix := newFixture(b)
210	labs := fix.sdk.Labels(makeLabels(1)...)
211	cnt := fix.sdk.NewInt64Counter("int64.counter")
212	handle := cnt.Bind(labs)
213
214	b.ResetTimer()
215
216	for i := 0; i < b.N; i++ {
217		handle.Add(ctx, 1)
218	}
219}
220
221func BenchmarkFloat64CounterAdd(b *testing.B) {
222	ctx := context.Background()
223	fix := newFixture(b)
224	labs := fix.sdk.Labels(makeLabels(1)...)
225	cnt := fix.sdk.NewFloat64Counter("float64.counter")
226
227	b.ResetTimer()
228
229	for i := 0; i < b.N; i++ {
230		cnt.Add(ctx, 1.1, labs)
231	}
232}
233
234func BenchmarkFloat64CounterHandleAdd(b *testing.B) {
235	ctx := context.Background()
236	fix := newFixture(b)
237	labs := fix.sdk.Labels(makeLabels(1)...)
238	cnt := fix.sdk.NewFloat64Counter("float64.counter")
239	handle := cnt.Bind(labs)
240
241	b.ResetTimer()
242
243	for i := 0; i < b.N; i++ {
244		handle.Add(ctx, 1.1)
245	}
246}
247
248// Gauges
249
250func BenchmarkInt64GaugeAdd(b *testing.B) {
251	ctx := context.Background()
252	fix := newFixture(b)
253	labs := fix.sdk.Labels(makeLabels(1)...)
254	gau := fix.sdk.NewInt64Gauge("int64.gauge")
255
256	b.ResetTimer()
257
258	for i := 0; i < b.N; i++ {
259		gau.Set(ctx, int64(i), labs)
260	}
261}
262
263func BenchmarkInt64GaugeHandleAdd(b *testing.B) {
264	ctx := context.Background()
265	fix := newFixture(b)
266	labs := fix.sdk.Labels(makeLabels(1)...)
267	gau := fix.sdk.NewInt64Gauge("int64.gauge")
268	handle := gau.Bind(labs)
269
270	b.ResetTimer()
271
272	for i := 0; i < b.N; i++ {
273		handle.Set(ctx, int64(i))
274	}
275}
276
277func BenchmarkFloat64GaugeAdd(b *testing.B) {
278	ctx := context.Background()
279	fix := newFixture(b)
280	labs := fix.sdk.Labels(makeLabels(1)...)
281	gau := fix.sdk.NewFloat64Gauge("float64.gauge")
282
283	b.ResetTimer()
284
285	for i := 0; i < b.N; i++ {
286		gau.Set(ctx, float64(i), labs)
287	}
288}
289
290func BenchmarkFloat64GaugeHandleAdd(b *testing.B) {
291	ctx := context.Background()
292	fix := newFixture(b)
293	labs := fix.sdk.Labels(makeLabels(1)...)
294	gau := fix.sdk.NewFloat64Gauge("float64.gauge")
295	handle := gau.Bind(labs)
296
297	b.ResetTimer()
298
299	for i := 0; i < b.N; i++ {
300		handle.Set(ctx, float64(i))
301	}
302}
303
304// Measures
305
306func benchmarkInt64MeasureAdd(b *testing.B, name string) {
307	ctx := context.Background()
308	fix := newFixture(b)
309	labs := fix.sdk.Labels(makeLabels(1)...)
310	mea := fix.sdk.NewInt64Measure(name)
311
312	b.ResetTimer()
313
314	for i := 0; i < b.N; i++ {
315		mea.Record(ctx, int64(i), labs)
316	}
317}
318
319func benchmarkInt64MeasureHandleAdd(b *testing.B, name string) {
320	ctx := context.Background()
321	fix := newFixture(b)
322	labs := fix.sdk.Labels(makeLabels(1)...)
323	mea := fix.sdk.NewInt64Measure(name)
324	handle := mea.Bind(labs)
325
326	b.ResetTimer()
327
328	for i := 0; i < b.N; i++ {
329		handle.Record(ctx, int64(i))
330	}
331}
332
333func benchmarkFloat64MeasureAdd(b *testing.B, name string) {
334	ctx := context.Background()
335	fix := newFixture(b)
336	labs := fix.sdk.Labels(makeLabels(1)...)
337	mea := fix.sdk.NewFloat64Measure(name)
338
339	b.ResetTimer()
340
341	for i := 0; i < b.N; i++ {
342		mea.Record(ctx, float64(i), labs)
343	}
344}
345
346func benchmarkFloat64MeasureHandleAdd(b *testing.B, name string) {
347	ctx := context.Background()
348	fix := newFixture(b)
349	labs := fix.sdk.Labels(makeLabels(1)...)
350	mea := fix.sdk.NewFloat64Measure(name)
351	handle := mea.Bind(labs)
352
353	b.ResetTimer()
354
355	for i := 0; i < b.N; i++ {
356		handle.Record(ctx, float64(i))
357	}
358}
359
360// MaxSumCount
361
362func BenchmarkInt64MaxSumCountAdd(b *testing.B) {
363	benchmarkInt64MeasureAdd(b, "int64.minmaxsumcount")
364}
365
366func BenchmarkInt64MaxSumCountHandleAdd(b *testing.B) {
367	benchmarkInt64MeasureHandleAdd(b, "int64.minmaxsumcount")
368}
369
370func BenchmarkFloat64MaxSumCountAdd(b *testing.B) {
371	benchmarkFloat64MeasureAdd(b, "float64.minmaxsumcount")
372}
373
374func BenchmarkFloat64MaxSumCountHandleAdd(b *testing.B) {
375	benchmarkFloat64MeasureHandleAdd(b, "float64.minmaxsumcount")
376}
377
378// DDSketch
379
380func BenchmarkInt64DDSketchAdd(b *testing.B) {
381	benchmarkInt64MeasureAdd(b, "int64.ddsketch")
382}
383
384func BenchmarkInt64DDSketchHandleAdd(b *testing.B) {
385	benchmarkInt64MeasureHandleAdd(b, "int64.ddsketch")
386}
387
388func BenchmarkFloat64DDSketchAdd(b *testing.B) {
389	benchmarkFloat64MeasureAdd(b, "float64.ddsketch")
390}
391
392func BenchmarkFloat64DDSketchHandleAdd(b *testing.B) {
393	benchmarkFloat64MeasureHandleAdd(b, "float64.ddsketch")
394}
395
396// Array
397
398func BenchmarkInt64ArrayAdd(b *testing.B) {
399	benchmarkInt64MeasureAdd(b, "int64.array")
400}
401
402func BenchmarkInt64ArrayHandleAdd(b *testing.B) {
403	benchmarkInt64MeasureHandleAdd(b, "int64.array")
404}
405
406func BenchmarkFloat64ArrayAdd(b *testing.B) {
407	benchmarkFloat64MeasureAdd(b, "float64.array")
408}
409
410func BenchmarkFloat64ArrayHandleAdd(b *testing.B) {
411	benchmarkFloat64MeasureHandleAdd(b, "float64.array")
412}
413