|
| 1 | +// Copyright The OpenTelemetry Authors |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +extern alias OpenTelemetryProtocol; |
| 5 | + |
| 6 | +using BenchmarkDotNet.Attributes; |
| 7 | +using Benchmarks.Helper; |
| 8 | +using OpenTelemetry.Logs; |
| 9 | +using OpenTelemetryProtocol::OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; |
| 10 | +using OpenTelemetryProtocol::OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; |
| 11 | + |
| 12 | +/* |
| 13 | +BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8457/25H2/2025Update/HudsonValley2) |
| 14 | +AMD Ryzen 7 9800X3D 4.70GHz, 1 CPU, 16 logical and 8 physical cores |
| 15 | +.NET SDK 10.0.301 |
| 16 | + [Host] : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v4 |
| 17 | + DefaultJob : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v4 |
| 18 | +
|
| 19 | +
|
| 20 | +| Method | AttributeCount | KvListAttributeCount | Mean | Error | StdDev | Median | Allocated | |
| 21 | +|--------------- |--------------- |--------------------- |---------:|---------:|---------:|---------:|----------:| |
| 22 | +| WriteLogRecord | 4 | 0 | 113.8 ns | 1.87 ns | 1.57 ns | 113.9 ns | 32 B | |
| 23 | +| WriteLogRecord | 4 | 2 | 168.9 ns | 3.15 ns | 8.31 ns | 166.4 ns | 32 B | |
| 24 | +| WriteLogRecord | 4 | 4 | 224.2 ns | 4.55 ns | 12.84 ns | 219.1 ns | 32 B | |
| 25 | +| WriteLogRecord | 4 | 8 | 254.4 ns | 5.11 ns | 10.99 ns | 252.6 ns | 32 B | |
| 26 | +| WriteLogRecord | 8 | 0 | 193.0 ns | 3.88 ns | 9.58 ns | 188.7 ns | 32 B | |
| 27 | +| WriteLogRecord | 8 | 2 | 239.4 ns | 2.89 ns | 2.26 ns | 239.3 ns | 32 B | |
| 28 | +| WriteLogRecord | 8 | 4 | 297.5 ns | 5.87 ns | 10.58 ns | 294.2 ns | 32 B | |
| 29 | +| WriteLogRecord | 8 | 8 | 340.9 ns | 8.21 ns | 23.03 ns | 332.2 ns | 32 B | |
| 30 | +| WriteLogRecord | 16 | 0 | 365.1 ns | 7.95 ns | 22.16 ns | 357.4 ns | 32 B | |
| 31 | +| WriteLogRecord | 16 | 2 | 427.9 ns | 11.96 ns | 34.70 ns | 417.5 ns | 32 B | |
| 32 | +| WriteLogRecord | 16 | 4 | 476.7 ns | 15.13 ns | 44.62 ns | 480.0 ns | 32 B | |
| 33 | +| WriteLogRecord | 16 | 8 | 471.3 ns | 9.11 ns | 12.16 ns | 466.0 ns | 32 B | |
| 34 | + */ |
| 35 | + |
| 36 | +namespace Benchmarks.Exporter; |
| 37 | + |
| 38 | +[MemoryDiagnoser(false)] |
| 39 | +public class ProtobufOtlpLogSerializerBenchmarks |
| 40 | +{ |
| 41 | + private static readonly KeyValuePair<string, object?>[] AllAttributes = |
| 42 | + [ |
| 43 | + new("http.request.method", "GET"), |
| 44 | + new("http.route", "/api/orders/{id}"), |
| 45 | + new("http.response.status_code", 200L), |
| 46 | + new("url.full", "https://shop.example.com/api/orders/42"), |
| 47 | + new("url.scheme", "https"), |
| 48 | + new("server.address", "shop.example.com"), |
| 49 | + new("server.port", 443L), |
| 50 | + new("client.address", "203.0.113.7"), |
| 51 | + new("user_agent.original", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"), |
| 52 | + new("network.protocol.version", "1.1"), |
| 53 | + new("enduser.id", "user-8f3a7b4c"), |
| 54 | + new("session.id", "9a8d1f3e-4b2c-4e15-9a4f-1b2c3d4e5f6a"), |
| 55 | + new("db.system", "postgresql"), |
| 56 | + new("db.namespace", "orders"), |
| 57 | + new("db.operation.name", "SELECT"), |
| 58 | + new("db.query.text", "SELECT * FROM orders WHERE id = $1"), |
| 59 | + new("messaging.system", "rabbitmq"), |
| 60 | + new("messaging.destination.name", "orders.events"), |
| 61 | + new("messaging.message.id", "msg-0abcdef1234567890"), |
| 62 | + new("exception.type", "System.TimeoutException"), |
| 63 | + new("exception.message", "The operation has timed out."), |
| 64 | + new("code.function", "ProcessOrder"), |
| 65 | + new("code.namespace", "Checkout.Api.Orders"), |
| 66 | + new("code.filepath", "/src/Checkout.Api/Orders/OrderProcessor.cs"), |
| 67 | + new("code.lineno", 142L), |
| 68 | + new("thread.id", 27L), |
| 69 | + new("thread.name", "worker-3"), |
| 70 | + new("retry.count", 2L), |
| 71 | + new("cache.hit", true), |
| 72 | + new("response.latency_ms", 18.7), |
| 73 | + new("feature.flag.new_checkout", true), |
| 74 | + new("tenant.id", "tenant-123456789012"), |
| 75 | + ]; |
| 76 | + |
| 77 | + private readonly byte[] buffer = new byte[64 * 1024]; |
| 78 | + private readonly SdkLimitOptions sdkLimitOptions = new(); |
| 79 | + private readonly ExperimentalOptions experimentalOptions = new(); |
| 80 | + private LogRecord logRecord = null!; |
| 81 | + |
| 82 | + [Params(4, 8, 16)] |
| 83 | + public int AttributeCount { get; set; } |
| 84 | + |
| 85 | + // 0 means no kvlist attribute is added; otherwise a single flat (depth-1) |
| 86 | + // kvlist attribute with this many entries is appended to the scalar ones. |
| 87 | + [Params(0, 2, 4, 8)] |
| 88 | + public int KvListAttributeCount { get; set; } |
| 89 | + |
| 90 | + [GlobalSetup] |
| 91 | + public void Setup() |
| 92 | + { |
| 93 | + this.logRecord = LogRecordHelper.CreateTestLogRecord(); |
| 94 | + |
| 95 | + var includeKvList = this.KvListAttributeCount > 0; |
| 96 | + var attributes = new KeyValuePair<string, object?>[this.AttributeCount + (includeKvList ? 1 : 0)]; |
| 97 | + |
| 98 | + var count = Math.Min(this.AttributeCount, AllAttributes.Length); |
| 99 | + for (var i = 0; i < count; i++) |
| 100 | + { |
| 101 | + attributes[i] = AllAttributes[i]; |
| 102 | + } |
| 103 | + |
| 104 | + for (var i = count; i < this.AttributeCount; i++) |
| 105 | + { |
| 106 | + attributes[i] = new KeyValuePair<string, object?>($"custom.attribute.{i}", Guid.NewGuid().ToString()); |
| 107 | + } |
| 108 | + |
| 109 | + if (includeKvList) |
| 110 | + { |
| 111 | + attributes[this.AttributeCount] = new KeyValuePair<string, object?>("kvlist", BuildKvList(this.KvListAttributeCount)); |
| 112 | + } |
| 113 | + |
| 114 | + this.logRecord.Attributes = attributes; |
| 115 | + } |
| 116 | + |
| 117 | + [Benchmark] |
| 118 | + public int WriteLogRecord() |
| 119 | + => ProtobufOtlpLogSerializer.WriteLogRecord(this.buffer, 0, this.sdkLimitOptions, this.experimentalOptions, this.logRecord); |
| 120 | + |
| 121 | + private static List<KeyValuePair<string, object?>> BuildKvList(int entryCount) |
| 122 | + { |
| 123 | + var list = new List<KeyValuePair<string, object?>>(entryCount); |
| 124 | + for (var i = 0; i < entryCount; i++) |
| 125 | + { |
| 126 | + object value = (i % 4) switch |
| 127 | + { |
| 128 | + 0 => "value", |
| 129 | + 1 => (long)i, |
| 130 | + 2 => i % 2 == 0, |
| 131 | + _ => i * 1.5, |
| 132 | + }; |
| 133 | + |
| 134 | + list.Add(new KeyValuePair<string, object?>($"key.{i}", value)); |
| 135 | + } |
| 136 | + |
| 137 | + return list; |
| 138 | + } |
| 139 | +} |
0 commit comments