Skip to content

feat: e2e SDK→Collector performance benchmark of window aggregation - #40

Merged
zzylol merged 14 commits into
mainfrom
9-performance-evaluation-of-e2e-otel
Mar 17, 2026
Merged

zzylol merged 14 commits into
mainfrom
9-performance-evaluation-of-e2e-otel

Conversation

@zzylol

@zzylol zzylol commented Mar 15, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds an end-to-end performance benchmark for the SDK→Collector pipeline across sketch types, and aligns the benchmark setup to reflect realistic aggregation behavior.

New files

  • opentelemetry-app/cmd/e2esdkbench/main.go: Go benchmark that drives the SDK under configurable load, measures bandwidth (loopback TX via /proc/net/dev), SDK heap/CPU (runtime.ReadMemStats + syscall.Getrusage), and writes per-second CSV + summary JSON per run.
  • otel_collector_benchmark/bench_sdk_e2e.sh: Shell orchestration script — iterates sketch types and rates, starts/stops the appropriate collector binary, monitors collector CPU/memory via ps, and aggregates results into aggregate_summary.csv.
  • opentelemetry-collector-contrib-patch/cmd/nopcol/config-bench.yaml: Nop collector config for the baseline (OTLP receive → nop processor → nop exporter).

SDK changes (opentelemetry-go-patch)

  • Removed unused Noop() aggregate builder.
  • Minor formatting cleanup in pipeline.go (no logic change).

Benchmark design

Sketch types in scope: baseline, ddsketch, kll, hll
(CountSketch and CountMinSketch dropped — CountSketch has no per-series aggregation support.)

Instruments: All modes use Float64Gauge. Sketch types attach a view to override the default LastValue aggregation with the sketch aggregation. Baseline uses no view — the gauge's natural LastValue sends one raw sample per series per export.

SDK side:

  • Workers record at 1s / samples_per_sec_per_series (e.g. every 20ms at 50 sps/s).
  • Sketch types: PeriodicReader exports every 1 second, so each exported sketch aggregates all samples recorded in that window.
  • Baseline: PeriodicReader exports at the worker rate (e.g. every 20ms), so every raw sample is sent individually with no aggregation.

Collector side:

  • Sketch processors run in mode: window with a 10s tumbling window, aggregating per series across 10 consecutive SDK exports before flushing.
  • Baseline uses nopcol: OTLP receive → nop processor → nop exporter (zero processing overhead).
  • max_recv_msg_size_mib: 64 on HLL collector for large per-series payloads at high rates.

Collector config changes (opentelemetry-collector-contrib-patch)

  • All sketch types in bench_sdk_e2e.sh now use config-window.yaml.
  • hllcol/config-window.yaml: add max_recv_msg_size_mib: 64, set drop_original: true.
  • countsketchcol/config-window.yaml: window_size 5s → 10s, add max_recv_msg_size_mib: 64, remove batch processor and debug exporter.
  • countminsketchcol/config-window.yaml: add max_recv_msg_size_mib: 64.
  • nopcol/builder-config.yaml: replace debugexporter/otlpexporter with nopexporter.
  • nopcol/config-bench.yaml: nop processor + nop exporter pipeline (no batch, no debug).

Benchmark results

Config: 60s duration · 1,000 series · rates: 1K / 10K / 100K / 1M MPS · window mode (10s tumbling window)

BW = gRPC wire bytes from SDK. SDK CPU% = user+sys over wall time. Collector CPU/Mem sampled via ps every 1s.

E2E Benchmark Overview

SDK-side metrics

Sketch Rate MPS BW avg KB/s BW peak KB/s SDK Heap avg MB SDK Heap peak MB SDK CPU%
baseline 1,000 50.55 53.57 20.89 36.58 2.27%
baseline 10,000 517.11 519.57 21.70 39.41 20.63%
baseline 100,000 5,095.31 5,226.86 14.81 22.74 196.17%
baseline 1,000,000 4,441.10 4,925.59 15.39 20.22 496.91%
ddsketch 1,000 337.80 553.50 21.09 33.98 4.76%
ddsketch 10,000 1,284.41 1,887.04 21.87 39.31 18.36%
ddsketch 100,000 2,187.07 2,366.01 26.99 34.52 78.41%
ddsketch 1,000,000 2,312.95 2,399.55 15.31 19.49 382.73%
kll 1,000 334.19 444.56 15.54 21.76 3.20%
kll 10,000 857.36 1,381.25 14.61 21.22 10.75%
kll 100,000 1,212.08 1,602.03 11.59 15.35 55.00%
kll 1,000,000 1,267.59 1,496.60 11.96 15.52 305.75%
hll 1,000 15,375.57 16,134.54 87.86 157.38 15.19%
hll 10,000 15,847.89 16,126.94 102.22 161.92 19.95%
hll 100,000 15,852.71 16,148.59 77.39 111.93 65.87%
hll 1,000,000 15,848.31 16,140.86 27.77 33.98 450.81%

Collector-side metrics

Sketch Rate MPS Col CPU avg% Col CPU peak% Col Mem avg MB Col Mem peak MB
baseline 1,000 0.31% 0.40% 29.47 31.80
baseline 10,000 2.36% 2.70% 32.87 34.50
baseline 100,000 20.96% 25.40% 32.58 34.66
baseline 1,000,000 19.49% 23.20% 33.07 35.24
ddsketch 1,000 1.26% 3.80% 184.15 195.35
ddsketch 10,000 1.70% 4.00% 206.25 235.40
ddsketch 100,000 2.24% 4.00% 223.57 248.21
ddsketch 1,000,000 2.46% 4.00% 222.98 239.47
kll 1,000 0.52% 0.60% 33.29 36.60
kll 10,000 0.69% 0.80% 42.96 65.80
kll 100,000 0.79% 1.00% 59.58 73.05
kll 1,000,000 1.02% 1.20% 61.07 73.99
hll 1,000 8.29% 10.10% 162.17 193.31
hll 10,000 8.56% 10.20% 167.92 195.40
hll 100,000 8.67% 10.40% 168.28 203.57
hll 1,000,000 9.57% 11.40% 167.86 195.57

Key observations

  • Baseline SDK CPU scales linearly with rate (2% → 497%) with stable ~33 MB collector memory — pure OTLP overhead.
  • KLL has the lowest collector overhead of all sketch types: CPU stays under 1% across all rates, memory grows modestly (33 → 61 MB) as the compactor accumulates more levels. SDK CPU is 3–4× below DDSketch at equivalent rates.
  • DDSketch collector CPU is near-constant (1.3–2.5%) regardless of rate — the 10s window absorbs bursts. Collector memory is high (184–224 MB) and grows with rate, driven by the dynamic bucket store accumulating more distinct values per window.
  • HLL bandwidth is flat at ~15.8 MB/s regardless of rate — each of the 1,000 series carries a full 16 KB register array (precision=14, 16,384 registers) every export. Collector memory (~162–168 MB) is dominated by these large payloads in flight through the gRPC/pipeline/window-store layers simultaneously.

Test plan

  • Rebuild nopcol binary (BUILD_ALWAYS=1 ./bench_sdk_e2e.sh --sketch baseline)
  • Run ./bench_sdk_e2e.sh --sketch all --duration 60s --series 1000 --rates "1000 10000 100000 1000000"
  • Confirm all 4 sketch types × rates complete and write to aggregate_summary.csv
  • Confirm no ResourceExhausted errors in collector logs

🤖 Generated with Claude Code

@zzylol zzylol linked an issue Mar 15, 2026 that may be closed by this pull request
zzylol and others added 4 commits March 15, 2026 03:31
Adds a systematic end-to-end benchmarking harness for all five sketch
types (DDSketch, KLL, CountSketch, CountMinSketch, HLL) running in
sdkSketch mode, plus a raw-gauge baseline for comparison.

New files:
- opentelemetry-app/cmd/e2esdkbench/main.go
    Go benchmark program that measures the SDK process in isolation:
    • Bandwidth: exact gRPC wire bytes via stats.Handler on the SDK
      gRPC connection (OutPayload.WireLength)
    • Memory: heap alloc + sys sampled every second via runtime.ReadMemStats
    • CPU: user+sys process time via syscall.Getrusage delta
    Supports sketch types: ddsketch|kll|countsketch|countminsketch|hll|baseline
    Outputs per-second timeseries CSV and summary JSON per run.

- otel_collector_benchmark/bench_sdk_e2e.sh
    Orchestration script: iterates sketch types × load rates, starts the
    appropriate collector binary, monitors collector CPU/memory via ps,
    drives e2esdkbench, and emits a combined aggregate CSV + report table.

- opentelemetry-collector-contrib-patch/cmd/nopcol/config-bench.yaml
    Baseline collector config: OTLP receiver + nop processor + debug
    exporter — no sketch computation anywhere.

Config fixes (grpc max message size):
- countsketchcol/config-batch.yaml: max_recv_msg_size_mib: 64
- countminsketchcol/config-batch.yaml: max_recv_msg_size_mib: 64
- hllcol/config-bench.yaml: max_recv_msg_size_mib: 64
  CountSketch/CountMinSketch/HLL sketch payloads exceed the default
  4 MiB gRPC message limit at high export rates; raised to 64 MiB on
  both the SDK send side and collector receive side.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Applying a View with Aggregation=nil to a Float64Gauge suppresses
reporting for that instrument, causing the baseline to report 0 B/s
bandwidth. Skip the View entirely for the baseline case so the gauge
uses its natural LastValue aggregation and exports normally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two fixes for the e2esdkbench bandwidth measurement:

1. Replace grpc.WithStatsHandler (unreliable for non-sketch metric types
   in the patched SDK — OutPayload.WireLength was 0 for ExplicitBucket-
   Histogram) with /proc/net/dev loopback TX byte delta. This counts all
   bytes written to the TCP socket during the run, regardless of gRPC
   internals, and works uniformly for baseline and all sketch types.

2. Baseline mode now uses AggregationExplicitBucketHistogram with an
   explicit view, matching the same export path as sketch types. This
   ensures the instrument is registered and exported, producing measurable
   bandwidth (~2.8 MB/s at 10k MPS vs ~866 KB/s for DDSketch at 10k MPS,
   as expected since buckets are larger than sketch serialization).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zzylol

zzylol commented Mar 15, 2026

Copy link
Copy Markdown
Contributor Author

@GnaneshGnani FYI

…ate parameterization)

- Refactor e2esdkbench CLI: replace --workers/--hosts/--metrics/--interval with
  --series and --samples-per-sec-per-series for clearer throughput control
- Update bench_sdk_e2e.sh to sweep 1000 series × 1/10/100/1000 samples/s/series
- Add sketch aggregation types to opentelemetry-go-patch (DDSketch, KLL,
  CountSketch, CountMinSketch, HLL): aggregation.go, pipeline.go, aggregate.go,
  metricdata/data.go, OTLP exporter transform
- Update opentelemetry-app go.mod to pull sketch deps (sketches-go, sketchlib-go)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zzylol
zzylol force-pushed the 9-performance-evaluation-of-e2e-otel branch from 8ac8f98 to a02eece Compare March 17, 2026 17:17
zzylol and others added 6 commits March 17, 2026 13:21
- baseline: switch SDK from ExplicitBucketHistogram to Float64Gauge
  (LastValue) so raw samples are sent without aggregation
- nopcol: replace debug exporter with true nop exporter and remove
  batch processor for a clean pass-through sink
- all sketch types (ddsketch, kll, hll, countsketch, countminsketch):
  decouple readerInterval (1s) from workerInterval (1s/sps) so each
  exported sketch aggregates all samples in a 1-second window

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…regation

- bench_sdk_e2e.sh: all sketch types now use config-window.yaml
- countsketch: window_size 5s→10s, add max_recv_msg_size_mib: 64,
  remove batch processor and debug exporter
- countminsketch: add max_recv_msg_size_mib: 64 (window config)
- hll: add max_recv_msg_size_mib: 64, set drop_original: true (window config)
- ddsketch/kll window configs already correct (window_duration: 10s)

Note: CountSketch processor aggregates globally by metric name and host
name (two shared sketches), not per individual series — this is a design
limitation of the processor unrelated to this change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both have design limitations (countsketch: no per-series aggregation;
countminsketch: dropped for scope reduction). Benchmark now covers
baseline, ddsketch, kll, and hll only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Large log file should not be tracked in version control.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All sketch types and baseline now record via Float64Gauge.
Sketch aggregation is applied via a view override (not by instrument
type), so the instrument type no longer needs to differ between modes.
Removes useHistogram flag and the Float64Histogram code path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Baseline reader interval = workerInterval (e.g. 20ms at 50 sps/s) so
each raw gauge sample is exported individually. Sketch types keep the
1s reader interval so each sketch aggregates a full second of samples.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zzylol zzylol self-assigned this Mar 17, 2026
zzylol and others added 2 commits March 17, 2026 14:14
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zzylol zzylol changed the title feat: e2e SDK→Collector performance benchmark with baseline feat: e2e SDK→Collector performance benchmark Mar 17, 2026
5-panel overview (bandwidth, SDK CPU, SDK memory, collector memory,
collector CPU) across all 4 sketch types × 4 rates (1K–1M MPS).
Generated from aggregate_summary.csv (60s, 1000 series, window mode).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zzylol zzylol changed the title feat: e2e SDK→Collector performance benchmark feat: e2e SDK→Collector performance benchmark of window aggregation Mar 17, 2026
@zzylol
zzylol merged commit ac83093 into main Mar 17, 2026
@zzylol
zzylol deleted the 9-performance-evaluation-of-e2e-otel branch March 17, 2026 21:32
SieDeta pushed a commit that referenced this pull request Apr 17, 2026
…2e-otel

feat: e2e SDK→Collector performance benchmark of window aggregation
zzylol added a commit that referenced this pull request May 5, 2026
…ugh wire_alpha() (#247)

Pairs with asap_sketchlib PR #40, which aligns
`asap_sketchlib::DdSketch::update`'s bucket-store growth with
`sketchlib-go`'s `Buckets.ensure` (chunks of 128, half-chunk-centered
seed) and adds a `wire_alpha()` accessor that returns the
gamma-roundtripped alpha matching Go's `SerializePortable` output.

This commit:
- Updates `DDSketchWrapper::build_state` to populate the proto's
  `alpha` field via `self.sk.wire_alpha()` instead of `self.sk.alpha`,
  so the emitted `DDSketchState.alpha` bytes match Go's. Without the
  roundtrip, even with the chunked-128 store layout in place, the
  proto bytes still diverge on the very first field.
- Removes the `#[ignore]` from
  `cross_language_parity::ddsketch_byte_parity_with_go`. With the
  upstream fix in place and the wrapper routing through `wire_alpha()`,
  `cargo test --test cross_language_parity ddsketch_byte_parity_with_go`
  now passes byte-equality against the
  `integration/parity/golden/ddsketch_envelope.bin` fixture.

KLL / HLL / CountSketch / CMS parity tests remain `#[ignore]`'d —
their divergences are tracked in the same upstream issue and will be
addressed in subsequent asap_sketchlib PRs.

Refs #243.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

performance evaluation of e2e otel

1 participant