opentelemetry-dotnet/test/Benchmarks/Metrics/MetricCollectBenchmarks.cs

128 lines
4.7 KiB
C#

// <copyright file="MetricCollectBenchmarks.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System.Diagnostics.Metrics;
using BenchmarkDotNet.Attributes;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Tests;
/*
BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.23424.1000)
Intel Core i7-9700 CPU 3.00GHz, 1 CPU, 8 logical and 8 physical cores
.NET SDK=7.0.203
[Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
DefaultJob : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
| Method | UseWithRef | Mean | Error | StdDev | Gen0 | Allocated |
|-------- |----------- |---------:|---------:|---------:|-------:|----------:|
| Collect | False | 16.24 us | 0.233 us | 0.621 us | 0.0153 | 160 B |
| Collect | True | 12.94 us | 0.092 us | 0.081 us | 0.0153 | 160 B |
*/
namespace Benchmarks.Metrics
{
public class MetricCollectBenchmarks
{
private readonly string[] dimensionValues = new string[] { "DimVal1", "DimVal2", "DimVal3", "DimVal4", "DimVal5", "DimVal6", "DimVal7", "DimVal8", "DimVal9", "DimVal10" };
// TODO: Confirm if this needs to be thread-safe
private readonly Random random = new();
private Counter<double> counter;
private MeterProvider provider;
private Meter meter;
private CancellationTokenSource token;
private BaseExportingMetricReader reader;
private Task writeMetricTask;
[Params(false, true)]
public bool UseWithRef { get; set; }
[GlobalSetup]
public void Setup()
{
var metricExporter = new TestExporter<Metric>(ProcessExport);
void ProcessExport(Batch<Metric> batch)
{
double sum = 0;
foreach (var metric in batch)
{
if (this.UseWithRef)
{
// The performant way of iterating.
foreach (ref readonly var metricPoint in metric.GetMetricPoints())
{
sum += metricPoint.GetSumDouble();
}
}
else
{
// The non-performant way of iterating.
// This is still "correct", but less performant.
foreach (var metricPoint in metric.GetMetricPoints())
{
sum += metricPoint.GetSumDouble();
}
}
}
}
this.reader = new BaseExportingMetricReader(metricExporter)
{
TemporalityPreference = MetricReaderTemporalityPreference.Cumulative,
};
this.meter = new Meter(Utils.GetCurrentMethodName());
this.provider = Sdk.CreateMeterProviderBuilder()
.AddMeter(this.meter.Name)
.AddReader(this.reader)
.Build();
this.counter = this.meter.CreateCounter<double>("counter");
this.token = new CancellationTokenSource();
this.writeMetricTask = new Task(() =>
{
while (!this.token.IsCancellationRequested)
{
var tag1 = new KeyValuePair<string, object>("DimName1", this.dimensionValues[this.random.Next(0, 10)]);
var tag2 = new KeyValuePair<string, object>("DimName2", this.dimensionValues[this.random.Next(0, 10)]);
var tag3 = new KeyValuePair<string, object>("DimName3", this.dimensionValues[this.random.Next(0, 10)]);
this.counter.Add(100.00, tag1, tag2, tag3);
}
});
this.writeMetricTask.Start();
}
[GlobalCleanup]
public void Cleanup()
{
this.token.Cancel();
this.token.Dispose();
this.writeMetricTask.Wait();
this.meter.Dispose();
this.provider.Dispose();
}
[Benchmark]
public void Collect()
{
this.reader.Collect();
}
}
}