Skip to content

test(trace-utils): add VecMap microbenchmarks#2126

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 3 commits into
mainfrom
yannham/microbenches-vecmap
Jun 19, 2026
Merged

test(trace-utils): add VecMap microbenchmarks#2126
gh-worker-dd-mergequeue-cf854d[bot] merged 3 commits into
mainfrom
yannham/microbenches-vecmap

Conversation

@yannham

@yannham yannham commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds criterion microbenchmarks for VecMap (libdd-trace-utils/src/span/vec_map.rs), the linear-scan ordered map backing each span's meta and metrics. The benches isolate the datastructure's core operations across representative map sizes (4, 8, 16, 32, 64), using BytesString keys to match real span usage:

  • insert (construction path), get (hit and miss), get_mut, contains_key
  • iter (encode path)
  • dedup (runs once per span on decode), with and without duplicate keys
  • as_deduped_map (encode path), both the cheap borrow case and the allocating fallback

A new opt-in bench-internals feature (mirroring libdd-sampling) gates the bench via required-features so it is not built by default. The change is additive to Cargo.toml.

Motivation

VecMap is a swappable datastructure (could be a hashmap/btree) that runs on the tracer hot path, but existing trace-utils benches only measure end-to-end msgpack encode/decode and never isolate it. Since it is linear-scan, the size-vs-operation curve is exactly what should be measured so the backing-structure choice can be revisited with data.

Additional Notes

VecMap and the span module are already pub, so bench-internals does not gate any source visibility; it is purely an opt-in switch keeping the bench out of the default build, consistent with libdd-sampling. Data is fully deterministic (no randomness).

How to test the change?

cargo bench -p libdd-trace-utils --features bench-internals --bench vec_map_bench --no-run
cargo bench -p libdd-trace-utils --features bench-internals --bench vec_map_bench

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Clippy Allow Annotation Report

Comparing clippy allow annotations between branches:

  • Base Branch: origin/main
  • PR Branch: origin/yannham/microbenches-vecmap

Summary by Rule

Rule Base Branch PR Branch Change

Annotation Counts by File

File Base Branch PR Branch Change

Annotation Stats by Crate

Crate Base Branch PR Branch Change
clippy-annotation-reporter 5 5 No change (0%)
datadog-ffe-ffi 1 1 No change (0%)
datadog-ipc 22 21 ✅ -1 (-4.5%)
datadog-live-debugger 4 4 No change (0%)
datadog-live-debugger-ffi 10 10 No change (0%)
datadog-profiling-replayer 4 4 No change (0%)
datadog-sidecar 45 45 No change (0%)
libdd-common 13 13 No change (0%)
libdd-common-ffi 12 12 No change (0%)
libdd-data-pipeline 5 5 No change (0%)
libdd-ddsketch 2 2 No change (0%)
libdd-dogstatsd-client 1 1 No change (0%)
libdd-profiling 13 13 No change (0%)
libdd-remote-config 3 3 No change (0%)
libdd-telemetry 20 20 No change (0%)
libdd-tinybytes 4 4 No change (0%)
libdd-trace-normalization 2 2 No change (0%)
libdd-trace-obfuscation 3 3 No change (0%)
libdd-trace-stats 1 1 No change (0%)
libdd-trace-utils 11 12 ⚠️ +1 (+9.1%)
Total 181 181 No change (0%)

About This Report

This report tracks Clippy allow annotations for specific rules, showing how they've changed in this PR. Decreasing the number of these annotations generally improves code quality.

@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 73.84% (+0.47%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: c8eb066 | Docs | Datadog PR Page | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Artifact Size Benchmark Report

aarch64-alpine-linux-musl
Artifact Baseline Commit Change
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.so 7.76 MB 7.76 MB 0% (0 B) 👌
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.a 84.02 MB 84.02 MB 0% (0 B) 👌
aarch64-unknown-linux-gnu
Artifact Baseline Commit Change
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.36 MB 10.36 MB 0% (0 B) 👌
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.a 95.13 MB 95.13 MB 0% (0 B) 👌
libdatadog-x64-windows
Artifact Baseline Commit Change
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.dll 24.93 MB 24.93 MB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.lib 87.33 KB 87.33 KB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.pdb 181.51 MB 181.51 MB 0% (0 B) 👌
/libdatadog-x64-windows/debug/static/datadog_profiling_ffi.lib 928.21 MB 928.22 MB +0% (+3.73 KB) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.dll 8.12 MB 8.12 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.lib 87.33 KB 87.33 KB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.pdb 24.03 MB 24.03 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/static/datadog_profiling_ffi.lib 47.96 MB 47.96 MB 0% (0 B) 👌
libdatadog-x86-windows
Artifact Baseline Commit Change
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.dll 21.62 MB 21.62 MB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.lib 88.71 KB 88.71 KB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.pdb 185.58 MB 185.60 MB +.01% (+24.00 KB) 🔍
/libdatadog-x86-windows/debug/static/datadog_profiling_ffi.lib 921.15 MB 921.16 MB +0% (+3.69 KB) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.dll 6.27 MB 6.27 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.lib 88.71 KB 88.71 KB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.pdb 25.76 MB 25.76 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/static/datadog_profiling_ffi.lib 45.59 MB 45.59 MB 0% (0 B) 👌
x86_64-alpine-linux-musl
Artifact Baseline Commit Change
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.a 74.91 MB 74.91 MB 0% (0 B) 👌
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.so 8.61 MB 8.61 MB 0% (0 B) 👌
x86_64-unknown-linux-gnu
Artifact Baseline Commit Change
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.a 90.33 MB 90.33 MB 0% (0 B) 👌
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.48 MB 10.48 MB 0% (0 B) 👌

@yannham
yannham force-pushed the yannham/microbenches-vecmap branch 3 times, most recently from 4111119 to bc3de29 Compare June 17, 2026 13:47
yannham and others added 2 commits June 17, 2026 15:58
Add criterion microbenchmarks isolating `VecMap`, the linear-scan ordered
map backing each span's `meta` and `metrics`. Covers insert, get (hit/miss),
get_mut, contains_key, iter, dedup (with and without duplicates) and
as_deduped_map (borrow vs. allocating path) across representative map sizes
(4-64), using `BytesString` keys to match real span usage.

Gated behind a new opt-in `bench-internals` feature (mirroring
`libdd-sampling`) so the bench is not built by default.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@yannham
yannham force-pushed the yannham/microbenches-vecmap branch from bc3de29 to 041db43 Compare June 17, 2026 13:58
@yannham
yannham marked this pull request as ready for review June 17, 2026 13:58
@yannham
yannham requested review from a team as code owners June 17, 2026 13:58
Comment thread libdd-trace-utils/Cargo.toml Outdated
Comment on lines +97 to +100
# Opt-in switch for crate-internal microbenchmarks (e.g. `vec_map_bench`). Mirrors the
# `bench-internals` feature in `libdd-sampling`; the bench targets it via `required-features` so
# they are not built by default. Not intended for downstream consumers.
bench-internals = []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit verbose, Opt-in switch for crate-internal microbenchmarksis sufficient

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I caught one instance but not this one 🙃

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we run the same bench cases on HashMaps to prove that VecMaps are more performance ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! I thought about it, but I'm not sure criterion is the right tool for that, at least used naively. In particular it would report improvements or degradation for the stdlib hashmap for future PR, which is not very relevant for libdatadog devs.

Maybe what we could do is to make a criterion benchmark when we measure the difference or the ratio?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(in particular my current goal for micro and macro benches is to be able to measure the evolution of perf: did a change made things better or worse, and how? I feel like comparing VecMap to HashMap is a bit different, and could be done as a one-off thing on the side)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it doesn't even need to be committed even 👍🏽

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you're right, we should do that. That could also gives us hint as to the range where VecMap should be preferred against HashMap or not. Is it like 1 to 50 elements and max duplicate ratio 10%, or is it actually fast until 250 elements and can tolerate 25% duplicates? Those are interesting question to answer.

Comment thread libdd-trace-utils/Cargo.toml Outdated
iunanua added a commit that referenced this pull request Jul 7, 2026
…ibdd-data-pipeline, libdd-li... (#2201)

# Release proposal for libdd-capabilities-impl, libdd-common,
libdd-data-pipeline, libdd-library-config, libdd-remote-config,
libdd-sampling, libdd-telemetry, libdd-tinybytes, libdd-trace-utils and
their dependencies

This PR contains version bumps based on public API changes and commits
since last release.

## libdd-capabilities
**Next version:** `2.1.0`
**Semver bump:** `minor`
**Tag:** `libdd-capabilities-v2.1.0`

### Commits

- feat(data-pipeline)!: add stdout log trace exporter (#2074)

## libdd-common
**Next version:** `5.1.0`
**Semver bump:** `minor`
**Tag:** `libdd-common-v5.1.0`

### Commits

- refactor(clippy): prefer core and alloc imports (#2196)
- fix: update rustls-webpki to 0.103.13 (#2187)
- fix: update anyhow for unsoundness (#2186)
- feat(machine id): Add helpers in ddcommon to fetch the machine UUID l…
(#2163)

## libdd-ddsketch
**Next version:** `1.1.0`
**Semver bump:** `minor`
**Tag:** `libdd-ddsketch-v1.1.0`

### Commits

- feat(data-pipeline)!: export client-computed span stats as OTLP trace
metrics (#2067)
- test(ddsketch): add microbenchmarks for add/encode/collapse (#2125)

## libdd-trace-protobuf
**Next version:** `4.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-protobuf-v4.0.0`

### Commits

- chore!: update protobufs to be in sync with datadog-agent (#2180)
- feat(stats)!: add whole key cardinality limit (#2158)
- feat(remote-config)!: use the proto file from the agent (#2165)
- feat(data-pipeline): OTLP HTTP/protobuf trace export (#2115)

## libdd-capabilities-impl
**Next version:** `3.0.0`
**Semver bump:** `major`
**Tag:** `libdd-capabilities-impl-v3.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.1.0 → ^5.1.0

### Commits

- feat(data-pipeline)!: add stdout log trace exporter (#2074)

## libdd-library-config
**Next version:** `3.0.0`
**Semver bump:** `major`
**Tag:** `libdd-library-config-v3.0.0`

### ⚠️ major bump forced due to:

- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0

### Commits

- refactor(clippy): prefer core and alloc imports (#2196)
- feat(library-config)!: caller-supplied threadlocal schema and extra
process-context attributes (#2162)
- fix(otel-thread-ctx): put the threadlocal attributes at the right
place in the context (#2167)

## libdd-remote-config
**Next version:** `2.0.0`
**Semver bump:** `major`
**Tag:** `libdd-remote-config-v2.0.0`

### ⚠️ major bump forced due to:

- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0

### Commits

- refactor(libdd-remote-config)!: hide Target inner properties so they
are not leaked (#2182)
- feat(remote-config)!: use the proto file from the agent (#2165)
- refactor(rc): reexport Endpoint and Tag common types (#2147)

## libdd-trace-normalization
**Next version:** `3.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-normalization-v3.0.0`

### ⚠️ major bump forced due to:

- `libdd-trace-protobuf`: ^3.0.1 → ^4.0.0

### Commits

- feat(data-pipeline)!: CSS Trace Filters (#1985)

## libdd-shared-runtime
**Next version:** `2.0.0`
**Semver bump:** `major`
**Tag:** `libdd-shared-runtime-v2.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.1.0 → ^5.1.0

### Commits

- feat(shared-runtime)!: SharedRuntime Borrowed & Owned mode (#2061)
- feat(shared-runtime)!: use weak waker in trigger [APMSP-3371] (#2050)

## libdd-trace-utils
**Next version:** `9.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-utils-v9.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0

### Commits

- ci(miri): skip slow miri tests (#2188)
- chore!: update protobufs to be in sync with datadog-agent (#2180)
- feat(data-pipeline): add agentless export (#2081)
- feat(data-pipeline)!: add stdout log trace exporter (#2074)
- feat(data-pipeline): OTLP HTTP/protobuf trace export (#2115)
- feat(otlp)!: Export OTLP spans with attribute-level OTel compatibility
(#2091)
- test(trace-utils): add V05 msgpack decode microbenchmark (#2127)
- feat(data-pipeline)!: export client-computed span stats as OTLP trace
metrics (#2067)
- test(trace-utils): add VecMap microbenchmarks (#2126)
- chore(stats)!: submit p0 telemetry in stats (#2130)
- refactor(change-buffer)!: replace slot index with span_id, fix segment
isolation (#2105)
- feat(data-pipeline)!: CSS Trace Filters (#1985)
- feat(trace-exporter): add v1 span and its encoder (#2039)
- fix(trace-utils): mark decoded span maps as deduped (#2110)
- feat(trace-utils)!: change buffer implementation (#2055)
- feat(native-spans)!: change buffer foundation (#2046)
- refactor(span)!: use VecMap for `meta`, `metrics` and `meta_struct`
for v04 spans (#2043)
- test: fix timeouts on heavily contended scenarios (#2093)

## libdd-telemetry
**Next version:** `6.0.0`
**Semver bump:** `major`
**Tag:** `libdd-telemetry-v6.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-shared-runtime`: ^1.0.0 → ^2.0.0

### Commits

- ci(miri): skip slow miri tests (#2188)
- refactor(libdd-telemetry)!: avoid leaking libdd-common types in the
public API (#2152)
- feat(shared-runtime)!: SharedRuntime Borrowed & Owned mode (#2061)

## libdd-trace-obfuscation
**Next version:** `5.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-obfuscation-v5.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0
- `libdd-trace-utils`: ^8.0.0 → ^9.0.0

### Commits

- refactor(clippy): prefer core and alloc imports (#2196)
- ci(miri): skip slow miri tests (#2188)
- fix: update anyhow for unsoundness (#2186)

## libdd-trace-stats
**Next version:** `6.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-stats-v6.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-shared-runtime`: ^1.0.0 → ^2.0.0
- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0
- `libdd-trace-utils`: ^8.0.0 → ^9.0.0

### Commits

- chore!: update protobufs to be in sync with datadog-agent (#2180)
- feat(stats)!: send telemetry for cardinality limits (#2159)
- feat(stats)!: add whole key cardinality limit (#2158)
- fix(trace-stats)!: add grpc_method to aggregation key (#2151)
- feat(shared-runtime)!: SharedRuntime Borrowed & Owned mode (#2061)
- feat(data-pipeline)!: export client-computed span stats as OTLP trace
metrics (#2067)
- refactor(span)!: use VecMap for `meta`, `metrics` and `meta_struct`
for v04 spans (#2043)

## libdd-data-pipeline
**Next version:** `7.0.0`
**Semver bump:** `major`
**Tag:** `libdd-data-pipeline-v7.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-shared-runtime`: ^1.0.0 → ^2.0.0
- `libdd-telemetry`: ^5.0.1 → ^6.0.0
- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0
- `libdd-trace-stats`: ^5.0.0 → ^6.0.0
- `libdd-trace-utils`: ^8.0.0 → ^9.0.0

### Commits

- feat(trace_exporter): enable telemetry in stats exporter (#2160)
- refactor(libdd-telemetry)!: avoid leaking libdd-common types in the
public API (#2152)
- feat(stats): emit canonical gRPC status name for OTLP
rpc.response.status_code (#2183)
- feat(data-pipeline): add agentless export (#2081)
- feat(stats)!: send telemetry for cardinality limits (#2159)
- feat(stats)!: add whole key cardinality limit (#2158)
- fix(trace-stats)!: add grpc_method to aggregation key (#2151)
- feat(data-pipeline)!: add stdout log trace exporter (#2074)
- feat(shared-runtime)!: SharedRuntime Borrowed & Owned mode (#2061)
- feat(data-pipeline): OTLP HTTP/protobuf trace export (#2115)
- feat(otlp)!: Export OTLP spans with attribute-level OTel compatibility
(#2091)
- feat(data-pipeline)!: export client-computed span stats as OTLP trace
metrics (#2067)
- chore(stats)!: submit p0 telemetry in stats (#2130)
- feat(data-pipeline)!: CSS Trace Filters (#1985)
- feat(shared-runtime)!: use weak waker in trigger [APMSP-3371] (#2050)
- refactor(span)!: use VecMap for `meta`, `metrics` and `meta_struct`
for v04 spans (#2043)
- feat(stats)!: add endpoint gating to client-side stats [APMSP-3361]
(#2040)

## libdd-dogstatsd-client
**Next version:** `4.0.0`
**Semver bump:** `major`
**Tag:** `libdd-dogstatsd-client-v4.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.1.0 → ^5.1.0


## libdd-sampling
**Next version:** `5.0.0`
**Semver bump:** `major`
**Tag:** `libdd-sampling-v5.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-trace-utils`: ^8.0.0 → ^9.0.0


[APMSP-3371]:
https://datadoghq.atlassian.net/browse/APMSP-3371?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: iunanua <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants