use criterion::{black_box, criterion_group, BatchSize, Criterion};
use libdd_trace_utils::msgpack_decoder;
use libdd_trace_utils::otlp_encoder::{
encode_otlp_json, encode_otlp_protobuf, map_traces_to_otlp, OtlpResourceInfo,
};
use serde_json::{json, Value};
fn generate_spans(num_spans: usize, trace_id: u64) -> Vec<Value> {
let root_span_id = 100_000_000_000 + (trace_id % 1_000_000);
(0..num_spans)
.map(|i| {
let span_id = root_span_id + i as u64;
let is_root = i == 0;
let parent_id = if is_root { 0 } else { root_span_id };
let mut meta = json!({
"http.method": "GET",
"http.url": "https://example.com/api/v1/users/12345",
"http.status_code": "200",
"env": "production",
"version": "1.2.3",
"component": "net/http",
});
if is_root {
meta["_dd.p.tid"] = json!("5b8efff798038103");
}
json!({
"service": "bench-service",
"name": "http.request",
"resource": "GET /api/v1/users",
"trace_id": trace_id,
"span_id": span_id,
"parent_id": parent_id,
"start": 1_544_712_660_000_000_000_i64 + i as i64,
"duration": 1_000_000,
"error": 0,
"meta": meta,
"metrics": { "_sampling_priority_v1": 1, "_dd.top_level": 1 },
"type": "web",
})
})
.collect()
}
fn generate_trace_chunks(num_chunks: usize, num_spans: usize) -> Vec<Vec<Value>> {
(0..num_chunks)
.map(|i| generate_spans(num_spans, 100_000_000_000 + i as u64))
.collect()
}
fn resource_info() -> OtlpResourceInfo {
let mut info = OtlpResourceInfo::default();
info.service = "bench-service".to_string();
info.env = "production".to_string();
info.app_version = "1.2.3".to_string();
info.language = "rust".to_string();
info.tracer_version = "9.9.9".to_string();
info.runtime_id = "11111111-2222-3333-4444-555555555555".to_string();
info
}
pub fn otlp_encoding_benches(c: &mut Criterion) {
let info = resource_info();
let (num_chunks, num_spans) = (1usize, 1000usize);
let id = format!("{num_chunks}x{num_spans}");
let bytes = rmp_serde::to_vec(&generate_trace_chunks(num_chunks, num_spans))
.expect("serialize fixture");
let (spans, _) = msgpack_decoder::v04::from_slice(bytes.as_slice()).expect("decode fixture");
c.bench_function(&format!("otlp/map_to_prost/{id}"), |b| {
b.iter_batched(
|| spans.clone(),
|s| black_box(map_traces_to_otlp(black_box(s), &info, false)),
BatchSize::SmallInput,
)
});
let req = map_traces_to_otlp(spans.clone(), &info, false);
c.bench_function(&format!("otlp/encode_protobuf/{id}"), |b| {
b.iter(|| black_box(encode_otlp_protobuf(black_box(&req))))
});
c.bench_function(&format!("otlp/encode_json/{id}"), |b| {
b.iter(|| black_box(encode_otlp_json(black_box(&req)).expect("json")))
});
c.bench_function(&format!("otlp/e2e_protobuf/{id}"), |b| {
b.iter_batched(
|| spans.clone(),
|s| {
let req = map_traces_to_otlp(s, &info, false);
black_box(encode_otlp_protobuf(&req))
},
BatchSize::SmallInput,
)
});
c.bench_function(&format!("otlp/e2e_json/{id}"), |b| {
b.iter_batched(
|| spans.clone(),
|s| {
let req = map_traces_to_otlp(s, &info, false);
black_box(encode_otlp_json(&req).expect("json"))
},
BatchSize::SmallInput,
)
});
}
criterion_group!(otlp_benches, otlp_encoding_benches);