diff --git a/test/Benchmarks/Benchmarks.csproj b/test/Benchmarks/Benchmarks.csproj index 084e1efd288..4a311573f13 100644 --- a/test/Benchmarks/Benchmarks.csproj +++ b/test/Benchmarks/Benchmarks.csproj @@ -21,6 +21,7 @@ + diff --git a/test/Benchmarks/Exporter/OtlpLogExporterBenchmarks.cs b/test/Benchmarks/Exporter/OtlpLogExporterBenchmarks.cs new file mode 100644 index 00000000000..06d980782c0 --- /dev/null +++ b/test/Benchmarks/Exporter/OtlpLogExporterBenchmarks.cs @@ -0,0 +1,154 @@ +// +// 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. +// + +#if !NETFRAMEWORK +extern alias OpenTelemetryProtocol; + +using BenchmarkDotNet.Attributes; +using Benchmarks.Helper; +using Grpc.Core; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using OpenTelemetry; +using OpenTelemetry.Internal; +using OpenTelemetry.Logs; +using OpenTelemetry.Tests; +using OpenTelemetryProtocol::OpenTelemetry.Exporter; +using OtlpCollector = OpenTelemetryProtocol::OpenTelemetry.Proto.Collector.Logs.V1; + +/* +BenchmarkDotNet v0.13.6, Windows 11 (10.0.22621.2134/22H2/2022Update/SunValley2) (Hyper-V) +AMD EPYC 7763, 1 CPU, 16 logical and 8 physical cores +.NET SDK 7.0.400 + [Host] : .NET 7.0.10 (7.0.1023.36312), X64 RyuJIT AVX2 + DefaultJob : .NET 7.0.10 (7.0.1023.36312), X64 RyuJIT AVX2 + + +| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | +|--------------------- |---------:|--------:|--------:|-------:|-------:|----------:| +| OtlpLogExporter_Http | 138.7 us | 2.08 us | 1.95 us | 0.4883 | 0.2441 | 9.85 KB | +| OtlpLogExporter_Grpc | 268.3 us | 2.57 us | 2.28 us | 0.4883 | - | 9.54 KB | +*/ + +namespace Benchmarks.Exporter; + +public class OtlpLogExporterBenchmarks +{ + private OtlpLogExporter exporter; + private LogRecord logRecord; + private CircularBuffer logRecordBatch; + + private IHost host; + private IDisposable server; + private string serverHost; + private int serverPort; + + [GlobalSetup(Target = nameof(OtlpLogExporter_Grpc))] + public void GlobalSetupGrpc() + { + this.host = new HostBuilder() + .ConfigureWebHostDefaults(webBuilder => webBuilder + .ConfigureKestrel(options => + { + options.ListenLocalhost(4317, listenOptions => listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2); + }) + .ConfigureServices(services => + { + services.AddGrpc(); + }) + .Configure(app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + }); + })) + .Start(); + + var options = new OtlpExporterOptions(); + this.exporter = new OtlpLogExporter(options); + + this.logRecord = LogRecordHelper.CreateTestLogRecord(); + this.logRecordBatch = new CircularBuffer(1); + this.logRecordBatch.Add(this.logRecord); + } + + [GlobalSetup(Target = nameof(OtlpLogExporter_Http))] + public void GlobalSetupHttp() + { + this.server = TestHttpServer.RunServer( + (ctx) => + { + ctx.Response.StatusCode = 200; + ctx.Response.OutputStream.Close(); + }, + out this.serverHost, + out this.serverPort); + + var options = new OtlpExporterOptions + { + Endpoint = new Uri($"http://{this.serverHost}:{this.serverPort}"), + Protocol = OtlpExportProtocol.HttpProtobuf, + }; + this.exporter = new OtlpLogExporter(options); + + this.logRecord = LogRecordHelper.CreateTestLogRecord(); + this.logRecordBatch = new CircularBuffer(1); + this.logRecordBatch.Add(this.logRecord); + } + + [GlobalCleanup(Target = nameof(OtlpLogExporter_Grpc))] + public void GlobalCleanupGrpc() + { + this.exporter.Shutdown(); + this.exporter.Dispose(); + this.host.Dispose(); + } + + [GlobalCleanup(Target = nameof(OtlpLogExporter_Http))] + public void GlobalCleanupHttp() + { + this.exporter.Shutdown(); + this.exporter.Dispose(); + this.server.Dispose(); + } + + [Benchmark] + public void OtlpLogExporter_Http() + { + this.exporter.Export(new Batch(this.logRecordBatch, 1)); + } + + [Benchmark] + public void OtlpLogExporter_Grpc() + { + this.exporter.Export(new Batch(this.logRecordBatch, 1)); + } + + private sealed class MockLogService : OtlpCollector.LogsService.LogsServiceBase + { + private static OtlpCollector.ExportLogsServiceResponse response = new OtlpCollector.ExportLogsServiceResponse(); + + public override Task Export(OtlpCollector.ExportLogsServiceRequest request, ServerCallContext context) + { + return Task.FromResult(response); + } + } +} +#endif diff --git a/test/Benchmarks/Exporter/OtlpTraceExporterBenchmarks.cs b/test/Benchmarks/Exporter/OtlpTraceExporterBenchmarks.cs new file mode 100644 index 00000000000..a833e7073d4 --- /dev/null +++ b/test/Benchmarks/Exporter/OtlpTraceExporterBenchmarks.cs @@ -0,0 +1,156 @@ +// +// 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. +// + +#if !NETFRAMEWORK +extern alias OpenTelemetryProtocol; + +using System.Diagnostics; +using BenchmarkDotNet.Attributes; +using Benchmarks.Helper; +using Grpc.Core; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using OpenTelemetry; +using OpenTelemetry.Internal; +using OpenTelemetry.Tests; +using OpenTelemetryProtocol::OpenTelemetry.Exporter; +using OtlpCollector = OpenTelemetryProtocol::OpenTelemetry.Proto.Collector.Trace.V1; + +/* +BenchmarkDotNet v0.13.6, Windows 11 (10.0.22621.2134/22H2/2022Update/SunValley2) (Hyper-V) +AMD EPYC 7763, 1 CPU, 16 logical and 8 physical cores +.NET SDK 7.0.400 + [Host] : .NET 7.0.10 (7.0.1023.36312), X64 RyuJIT AVX2 + DefaultJob : .NET 7.0.10 (7.0.1023.36312), X64 RyuJIT AVX2 + + +| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | +|----------------------- |---------:|--------:|--------:|-------:|-------:|----------:| +| OtlpTraceExporter_Http | 139.4 us | 1.41 us | 1.32 us | 0.4883 | 0.2441 | 9.8 KB | +| OtlpTraceExporter_Grpc | 263.0 us | 3.47 us | 3.24 us | 0.4883 | - | 9.34 KB | +*/ + +namespace Benchmarks.Exporter; + +public class OtlpTraceExporterBenchmarks +{ + private OtlpTraceExporter exporter; + private Activity activity; + private CircularBuffer activityBatch; + + private IHost host; + private IDisposable server; + private string serverHost; + private int serverPort; + + [GlobalSetup(Target = nameof(OtlpTraceExporter_Grpc))] + public void GlobalSetupGrpc() + { + this.host = new HostBuilder() + .ConfigureWebHostDefaults(webBuilder => webBuilder + .ConfigureKestrel(options => + { + options.ListenLocalhost(4317, listenOptions => listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2); + }) + .ConfigureServices(services => + { + services.AddGrpc(); + }) + .Configure(app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + }); + })) + .Start(); + + var options = new OtlpExporterOptions(); + this.exporter = new OtlpTraceExporter(options); + + this.activity = ActivityHelper.CreateTestActivity(); + this.activityBatch = new CircularBuffer(1); + this.activityBatch.Add(this.activity); + } + + [GlobalSetup(Target = nameof(OtlpTraceExporter_Http))] + public void GlobalSetupHttp() + { + this.server = TestHttpServer.RunServer( + (ctx) => + { + ctx.Response.StatusCode = 200; + ctx.Response.OutputStream.Close(); + }, + out this.serverHost, + out this.serverPort); + + var options = new OtlpExporterOptions + { + Endpoint = new Uri($"http://{this.serverHost}:{this.serverPort}"), + Protocol = OtlpExportProtocol.HttpProtobuf, + }; + this.exporter = new OtlpTraceExporter(options); + + this.activity = ActivityHelper.CreateTestActivity(); + this.activityBatch = new CircularBuffer(1); + this.activityBatch.Add(this.activity); + } + + [GlobalCleanup(Target = nameof(OtlpTraceExporter_Grpc))] + public void GlobalCleanupGrpc() + { + this.exporter.Shutdown(); + this.exporter.Dispose(); + this.activity.Dispose(); + this.host.Dispose(); + } + + [GlobalCleanup(Target = nameof(OtlpTraceExporter_Http))] + public void GlobalCleanupHttp() + { + this.exporter.Shutdown(); + this.exporter.Dispose(); + this.server.Dispose(); + this.activity.Dispose(); + } + + [Benchmark] + public void OtlpTraceExporter_Http() + { + this.exporter.Export(new Batch(this.activityBatch, 1)); + } + + [Benchmark] + public void OtlpTraceExporter_Grpc() + { + this.exporter.Export(new Batch(this.activityBatch, 1)); + } + + private sealed class MockTraceService : OtlpCollector.TraceService.TraceServiceBase + { + private static OtlpCollector.ExportTraceServiceResponse response = new OtlpCollector.ExportTraceServiceResponse(); + + public override Task Export(OtlpCollector.ExportTraceServiceRequest request, ServerCallContext context) + { + return Task.FromResult(response); + } + } +} +#endif diff --git a/test/Benchmarks/Helper/LogRecordHelper.cs b/test/Benchmarks/Helper/LogRecordHelper.cs new file mode 100644 index 00000000000..57d3b1e6f5d --- /dev/null +++ b/test/Benchmarks/Helper/LogRecordHelper.cs @@ -0,0 +1,37 @@ +// +// 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. +// + +using Microsoft.Extensions.Logging; +using OpenTelemetry.Logs; + +namespace Benchmarks.Helper; + +internal class LogRecordHelper +{ + internal static LogRecord CreateTestLogRecord() + { + var items = new List(1); + using var factory = LoggerFactory.Create(builder => builder + .AddOpenTelemetry(loggerOptions => + { + loggerOptions.AddInMemoryExporter(items); + })); + + var logger = factory.CreateLogger("TestLogger"); + logger.LogInformation("Hello from {Food} {Price}.", "artichoke", 3.99); + return items[0]; + } +}