Benchmarks

View Source

Performance benchmarks for the instrument library, measuring throughput of core operations.

Running Benchmarks

# Run core benchmarks
./bench/run_bench.escript

# Run real-world scenario benchmarks
rebar3 compile
erl -pa _build/default/lib/*/ebin -noshell -eval \
  "c:c(\"bench/instrument_realworld_bench.erl\"), instrument_realworld_bench:run(), halt()."

# Run client tracing strategy benchmarks
erl -pa _build/default/lib/*/ebin -noshell -eval \
  "c:c(\"bench/instrument_client_bench.erl\"), instrument_client_bench:run(), halt()."

Results

Benchmarks run on Apple M1 Pro, OTP 28, 100,000 iterations per test (April 2026).

Counter Operations

OperationThroughputLatency
inc_counter/143.6M ops/sec0.023 us/op
inc_counter/236.4M ops/sec0.028 us/op
get_counter/142.0M ops/sec0.024 us/op

Gauge Operations

OperationThroughputLatency
set_gauge/238.1M ops/sec0.026 us/op
inc_gauge/147.6M ops/sec0.021 us/op
dec_gauge/145.8M ops/sec0.022 us/op
get_gauge/141.0M ops/sec0.024 us/op

Histogram Operations

OperationThroughputLatency
observe/222.1M ops/sec0.045 us/op
get/13.0M ops/sec0.328 us/op

OpenTelemetry Meter API

OperationThroughputLatency
meter:add/265.8M ops/sec0.015 us/op
meter:add/3 (with attrs)2.8M ops/sec0.352 us/op
meter:record/230.7M ops/sec0.033 us/op
meter:record/3 (with attrs)3.7M ops/sec0.267 us/op
meter:set/275.2M ops/sec0.013 us/op

OpenTelemetry Tracer API

OperationThroughputLatency
with_span/2294K ops/sec3.40 us/op
with_span/3 (with kind)298K ops/sec3.36 us/op
with_span + set_attributes288K ops/sec3.48 us/op
with_span + add_event308K ops/sec3.25 us/op
Nested spans (3 levels)184K ops/sec5.44 us/op
start_span/end_span312K ops/sec3.20 us/op

OpenTelemetry Logger Integration

OperationThroughputLatency
logger:info (no span)52.8M ops/sec0.019 us/op
logger:info (in span)48.2M ops/sec0.021 us/op
logger:info (with metadata)46.7M ops/sec0.021 us/op
instrument_logger:emit3.2M ops/sec0.316 us/op

Real-World Scenarios

Simulated production workloads to measure end-to-end performance.

DB Query Tracing

ScenarioThroughputLatency
DB query traced200K ops/sec5.01 us/op
DB query metrics only1.79M ops/sec0.56 us/op

Tracing overhead: ~4.5 us per operation.

HTTP Request Pipeline

ScenarioThroughputLatency
3 nested spans (auth + db + response)98K ops/sec10.2 us/op
1 span only189K ops/sec5.3 us/op
No tracing1.60M ops/sec0.62 us/op

Concurrent Load

  • 100 workers x 1,000 requests = 100,000 total
  • Result: 27,465 req/sec sustained throughput

Memory Impact

  • 100,000 spans with attributes and events
  • Throughput: 245,700 spans/sec
  • Memory overhead: negligible (GC-friendly design)

Client Tracing Strategies

Comparing different approaches to instrument client operations (DB, HTTP, etc).

Strategy Comparison

StrategyThroughputLatencyNotes
No tracing (baseline)442M ops/sec0.002 us/opReference
Manual with_span249K ops/sec4.02 us/opDirect tracer use
instrument_client:with_span259K ops/sec3.86 us/opHelper (slightly faster)
Full options249K ops/sec4.02 us/opWith target, statement, attrs
With sanitization94K ops/sec10.60 us/opRegex-based sanitization

Sanitization Performance

StrategyThroughputLatencyNotes
No sanitization600M ops/sec0.002 us/opReference
Default (short SQL)200K ops/sec5.00 us/op~60 chars
Default (long SQL)166K ops/sec6.04 us/op~200 chars
Custom placeholder196K ops/sec5.10 us/opUser-defined
Preserve $N params156K ops/sec6.42 us/opKeep $1, $2 placeholders
URL path sanitize452K ops/sec2.21 us/opSimple pattern match

Sampling Strategies

StrategyThroughputLatencyNotes
always_on (100%)284K ops/sec3.52 us/opAll spans recorded
always_off (0%)1.45M ops/sec0.69 us/opDropped spans are cheap
probability (50%)410K ops/sec2.44 us/opHalf sampled
probability (10%)847K ops/sec1.18 us/opLow sampling
probability (1%)1.21M ops/sec0.83 us/opVery low sampling
attribute (no rules)830K ops/sec1.20 us/opDefault ratio only
attribute (1 rule)409K ops/sec2.45 us/opSingle rule match
attribute (7 rules)1.15M ops/sec0.87 us/opMultiple rules, early exit

Trace Context Injection

StrategyThroughputLatencyNotes
No injection428M ops/sec0.002 us/opReference
SQL comment format2.15M ops/sec0.46 us/op/*traceparent=...*/
URL param format1.33M ops/sec0.75 us/op?traceparent=...
Custom format2.17M ops/sec0.46 us/opUser-defined delimiters
format_trace_comment/02.53M ops/sec0.40 us/opFormat only, no append

Pool Span Helpers

StrategyThroughputLatencyNotes
No pool tracking243M ops/sec0.004 us/opReference
Manual pool spans255K ops/sec3.93 us/opDirect tracer use
pool_acquire/release258K ops/sec3.88 us/opHelper functions
with_pool_span230K ops/sec4.35 us/opWrapped operation

Analysis

Metrics Performance

Counter and gauge operations achieve 38-48 million operations per second thanks to NIF-based atomic operations. The OpenTelemetry Meter API adds minimal overhead for operations without attributes (65-75M ops/sec).

Operations with attributes are slower (2.8-3.7M ops/sec) due to attribute map handling and vec metric creation, but still very fast for most use cases.

Tracing Performance

Span creation costs ~3.2-3.5 us per span due to:

  • Context management (process dictionary operations)
  • Span ID generation
  • Timestamp collection
  • Exporter callbacks

The library achieves ~300K spans/second for simple spans. Nested spans scale sub-linearly: 3 nested spans take ~5.4 us (not 3x single span time) due to context reuse.

Sampling Impact

Sampling dramatically reduces overhead:

  • always_off: 5x faster than always_on (dropped spans skip most work)
  • 1% sampling: Nearly as fast as always_off
  • Attribute-based: Scales well, early exit on rule match

Optimization Tips

  1. Use sampling in production - even 10% sampling reduces overhead by 3x
  2. Prefer counters over histograms when you only need counts
  3. Batch operations rather than creating many small spans
  4. Pre-create instruments at startup rather than on-demand
  5. Sanitization adds ~6-7 us - only use when necessary
  6. Attribute operations are slower - minimize attribute cardinality
  7. Dropped spans are 5x cheaper - sampling helps significantly