Skip to content

Commit 46a7cd6

Browse files
authored
perf: Add benchmarks to show the cost of attributes in Log (#2741)
1 parent 92303b6 commit 46a7cd6

File tree

2 files changed

+275
-0
lines changed

2 files changed

+275
-0
lines changed

opentelemetry-appender-tracing/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ name = "logs"
4444
harness = false
4545
required-features = ["spec_unstable_logs_enabled"]
4646

47+
[[bench]]
48+
name = "log-attributes"
49+
harness = false
50+
4751
[lib]
4852
bench = false
4953

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/*
2+
// Run this benchmark with:
3+
// cargo bench --bench log-attributes
4+
// Adding results in comments for a quick reference.
5+
// Apple M4 Pro
6+
// Total Number of Cores: 14 (10 performance and 4 efficiency)
7+
8+
| Test | Average time | Increment |
9+
|----------------------|--------------|-----------|
10+
| otel_0_attributes | 72 ns | - |
11+
| otel_1_attributes | 117 ns | +45 ns |
12+
| otel_2_attributes | 155 ns | +38 ns |
13+
| otel_3_attributes | 196 ns | +41 ns |
14+
| otel_4_attributes | 240 ns | +44 ns |
15+
| otel_5_attributes | 278 ns | +38 ns |
16+
| otel_6_attributes | 346 ns | +68 ns | // Array is full. 6th attribute causes vec! to be allocated
17+
| otel_7_attributes | 390 ns | +44 ns |
18+
| otel_8_attributes | 431 ns | +41 ns |
19+
| otel_9_attributes | 480 ns | +49 ns |
20+
| otel_10_attributes | 519 ns | +39 ns |
21+
| otel_11_attributes | 625 ns | +106 ns | // vec! initial capacity is 5. 11th attribute causes vec! to be reallocated
22+
| otel_12_attributes | 676 ns | +51 ns |
23+
*/
24+
25+
use criterion::{criterion_group, criterion_main, Criterion};
26+
use opentelemetry::InstrumentationScope;
27+
use opentelemetry_appender_tracing::layer as tracing_layer;
28+
use opentelemetry_sdk::error::OTelSdkResult;
29+
use opentelemetry_sdk::logs::{LogProcessor, SdkLogRecord, SdkLoggerProvider};
30+
use opentelemetry_sdk::Resource;
31+
#[cfg(not(target_os = "windows"))]
32+
use pprof::criterion::{Output, PProfProfiler};
33+
use tracing::error;
34+
use tracing_subscriber::prelude::*;
35+
use tracing_subscriber::Registry;
36+
37+
#[derive(Debug)]
38+
struct NoopProcessor;
39+
40+
impl LogProcessor for NoopProcessor {
41+
fn emit(&self, _: &mut SdkLogRecord, _: &InstrumentationScope) {}
42+
43+
fn force_flush(&self) -> OTelSdkResult {
44+
Ok(())
45+
}
46+
47+
fn shutdown(&self) -> OTelSdkResult {
48+
Ok(())
49+
}
50+
}
51+
52+
/// Creates a single benchmark for a specific number of attributes
53+
fn create_benchmark(c: &mut Criterion, num_attributes: usize) {
54+
let provider = SdkLoggerProvider::builder()
55+
.with_resource(
56+
Resource::builder_empty()
57+
.with_service_name("benchmark")
58+
.build(),
59+
)
60+
.with_log_processor(NoopProcessor)
61+
.build();
62+
63+
let ot_layer = tracing_layer::OpenTelemetryTracingBridge::new(&provider);
64+
let subscriber = Registry::default().with(ot_layer);
65+
66+
tracing::subscriber::with_default(subscriber, || {
67+
c.bench_function(&format!("otel_{}_attributes", num_attributes), |b| {
68+
b.iter(|| {
69+
// Dynamically generate the error! macro call based on the number of attributes
70+
match num_attributes {
71+
0 => {
72+
error!(
73+
name : "CheckoutFailed",
74+
message = "Unable to process checkout."
75+
);
76+
}
77+
1 => {
78+
error!(
79+
name : "CheckoutFailed",
80+
field1 = "field1",
81+
message = "Unable to process checkout."
82+
);
83+
}
84+
2 => {
85+
error!(
86+
name : "CheckoutFailed",
87+
field1 = "field1",
88+
field2 = "field2",
89+
message = "Unable to process checkout."
90+
);
91+
}
92+
3 => {
93+
error!(
94+
name : "CheckoutFailed",
95+
field1 = "field1",
96+
field2 = "field2",
97+
field3 = "field3",
98+
message = "Unable to process checkout."
99+
);
100+
}
101+
4 => {
102+
error!(
103+
name : "CheckoutFailed",
104+
field1 = "field1",
105+
field2 = "field2",
106+
field3 = "field3",
107+
field4 = "field4",
108+
message = "Unable to process checkout."
109+
);
110+
}
111+
5 => {
112+
error!(
113+
name : "CheckoutFailed",
114+
field1 = "field1",
115+
field2 = "field2",
116+
field3 = "field3",
117+
field4 = "field4",
118+
field5 = "field5",
119+
message = "Unable to process checkout."
120+
);
121+
}
122+
6 => {
123+
error!(
124+
name : "CheckoutFailed",
125+
field1 = "field1",
126+
field2 = "field2",
127+
field3 = "field3",
128+
field4 = "field4",
129+
field5 = "field5",
130+
field6 = "field6",
131+
message = "Unable to process checkout."
132+
);
133+
}
134+
7 => {
135+
error!(
136+
name : "CheckoutFailed",
137+
field1 = "field1",
138+
field2 = "field2",
139+
field3 = "field3",
140+
field4 = "field4",
141+
field5 = "field5",
142+
field6 = "field6",
143+
field7 = "field7",
144+
message = "Unable to process checkout."
145+
);
146+
}
147+
8 => {
148+
error!(
149+
name : "CheckoutFailed",
150+
field1 = "field1",
151+
field2 = "field2",
152+
field3 = "field3",
153+
field4 = "field4",
154+
field5 = "field5",
155+
field6 = "field6",
156+
field7 = "field7",
157+
field8 = "field8",
158+
message = "Unable to process checkout."
159+
);
160+
}
161+
9 => {
162+
error!(
163+
name : "CheckoutFailed",
164+
field1 = "field1",
165+
field2 = "field2",
166+
field3 = "field3",
167+
field4 = "field4",
168+
field5 = "field5",
169+
field6 = "field6",
170+
field7 = "field7",
171+
field8 = "field8",
172+
field9 = "field9",
173+
message = "Unable to process checkout."
174+
);
175+
}
176+
10 => {
177+
error!(
178+
name : "CheckoutFailed",
179+
field1 = "field1",
180+
field2 = "field2",
181+
field3 = "field3",
182+
field4 = "field4",
183+
field5 = "field5",
184+
field6 = "field6",
185+
field7 = "field7",
186+
field8 = "field8",
187+
field9 = "field9",
188+
field10 = "field10",
189+
message = "Unable to process checkout."
190+
);
191+
}
192+
11 => {
193+
error!(
194+
name : "CheckoutFailed",
195+
field1 = "field1",
196+
field2 = "field2",
197+
field3 = "field3",
198+
field4 = "field4",
199+
field5 = "field5",
200+
field6 = "field6",
201+
field7 = "field7",
202+
field8 = "field8",
203+
field9 = "field9",
204+
field10 = "field10",
205+
field11 = "field11",
206+
message = "Unable to process checkout."
207+
);
208+
}
209+
12 => {
210+
error!(
211+
name : "CheckoutFailed",
212+
field1 = "field1",
213+
field2 = "field2",
214+
field3 = "field3",
215+
field4 = "field4",
216+
field5 = "field5",
217+
field6 = "field6",
218+
field7 = "field7",
219+
field8 = "field8",
220+
field9 = "field9",
221+
field10 = "field10",
222+
field11 = "field11",
223+
field12 = "field12",
224+
message = "Unable to process checkout."
225+
);
226+
}
227+
_ => {
228+
// Fall back to 10 attributes for any higher number
229+
error!(
230+
name : "CheckoutFailed",
231+
field1 = "field1",
232+
field2 = "field2",
233+
field3 = "field3",
234+
field4 = "field4",
235+
field5 = "field5",
236+
field6 = "field6",
237+
field7 = "field7",
238+
field8 = "field8",
239+
field9 = "field9",
240+
field10 = "field10",
241+
message = "Unable to process checkout."
242+
);
243+
}
244+
}
245+
});
246+
});
247+
});
248+
}
249+
250+
fn criterion_benchmark(c: &mut Criterion) {
251+
// Run benchmarks for 0 to 12 attributes
252+
for num_attributes in 0..=12 {
253+
create_benchmark(c, num_attributes);
254+
}
255+
}
256+
257+
#[cfg(not(target_os = "windows"))]
258+
criterion_group! {
259+
name = benches;
260+
config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
261+
targets = criterion_benchmark
262+
}
263+
264+
#[cfg(target_os = "windows")]
265+
criterion_group! {
266+
name = benches;
267+
config = Criterion::default();
268+
targets = criterion_benchmark
269+
}
270+
271+
criterion_main!(benches);

0 commit comments

Comments
 (0)