feat(trace-utils)!: introduce VecMap datastructure#2022
Conversation
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: f502f56 | Docs | Datadog PR Page | Give us feedback! |
📚 Documentation Check Results📦
|
Clippy Allow Annotation ReportComparing clippy allow annotations between branches:
Summary by Rule
Annotation Counts by File
Annotation Stats by Crate
About This ReportThis 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. |
🔒 Cargo Deny Results📦
|
267605b to
b1718c4
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2022 +/- ##
==========================================
+ Coverage 72.83% 72.92% +0.08%
==========================================
Files 459 460 +1
Lines 76134 76396 +262
==========================================
+ Hits 55452 55711 +259
- Misses 20682 20685 +3
🚀 New features to boost your workflow:
|
Artifact Size Benchmark Reportaarch64-alpine-linux-musl
aarch64-unknown-linux-gnu
libdatadog-x64-windows
libdatadog-x86-windows
x86_64-alpine-linux-musl
x86_64-unknown-linux-gnu
|
b1718c4 to
64f4421
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 64f4421cf2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,260 @@ | |||
| // Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/ | |||
There was a problem hiding this comment.
Github doesn't support thread comments so putting my thoughts here to keep it clear.
Overall this seems like it could be very beneficial to perf, have you considered:
- Using a dependency that does it (ex: https://github.com/p-avital/vec-map-rs)
- Storing keys and values in separate Vecs which as explained in vec-map-rs's readme could make the key lookup even faster by packing them close together.
- Adding micro benchmarks to compare HashMap/BTreeMap/VecMap(/vec-map-rs) operations on different number of keys, size of keys and size of values ?
- Having a higher level hybrid struct that underneath uses a VecMap until a certain threshold in the number of keys is reached then switches to HashMap ? (might be overkill)
There was a problem hiding this comment.
could make the key lookup even faster by packing them close together.
Attribute lists are usually pretty small (less than 10s of elements), and we need to chase one pointer when iterating anyway to read the string, and we would need to do two allocations instead of one so I have doubts on the perf gain.
There was a problem hiding this comment.
Those are all very good points:
- There's always a balance when pulling in a new dependency. For libdatadog, the cons are size explosion, and supply chain attack, most notably. Since the code is really simple (it's mostly boilerplate to be honest), I felt like having a bespoke datastructure here was preferable. But I think both approaches are reasonable. Another thing with having our own is that we can customize it to our needs, typically the tombstone-like removal (well it could probably easily be implemented as a wrapper of vec-map-rs, so maybe not a great example). Meta and metrics are very special cases of maps, and we might be able to take advantage of that even further?
- As @paullegranddc mentioned, I don't think it's worth the complexity or even that it would be a win. We do not care that much for get already. But to keep in mind for potential evolutions?
- We plan to bench this in dd-trace-rs and other real usage. For now this code is dead, so it doesn't impact anything that exists already. But maybe having a bunch of micro benches doesn't hurt, and can be easily generated by an LLM. I'll look into it!
- Without thinking too much into it, I think this might be overkill for now. Simplicity, beside being beneficial for maintenance, usually wins in terms of performance in a surprisingly vast number of use-cases: less branching, no pointer chasing and cache misses, etc. We should bench real tracers and only think about a more complex scheme if it turns out there are largish maps that show unacceptable perf degradation.
| @@ -0,0 +1,260 @@ | |||
| // Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/ | |||
There was a problem hiding this comment.
could make the key lookup even faster by packing them close together.
Attribute lists are usually pretty small (less than 10s of elements), and we need to chase one pointer when iterating anyway to read the string, and we would need to do two allocations instead of one so I have doubts on the perf gain.
468db1d to
af7f610
Compare
Co-authored-by: Jules Wiriath <53870805+Aaalibaba42@users.noreply.github.com>
74358f8 to
7e38b23
Compare
2570e54 to
f502f56
Compare
What does this PR do?
This PR is the first of a series that backports the change buffer implementation for dd-trace-js from experimentation to production in libdatadog.
This PR introduces a replacement for
HashMap:VecMap. It's not used anywhere else; this is left for follow-up PRs, but the idea is to use it for themeta,metrics, andmeta_structfields of v04 spans.Motivation
The change-buffer module (introduced in subsequent PRs) experimentations showed that we spend quite some time expanding hashmaps and thus re-hashing during span reconstruction. Introducing a map-like data structure that is optimized for construction/insertion (which is the vast majority of operations) showed to be beneficial.
Even if not optimized for reads, we expect that given the small cardinality of meta and metrics in a span (< 20 entries in general), linear scan is actually going to perform well because of cache locality. Removal is one operation that is made quite more expensive (although still linear), but can be mitigated later with tombstones (also still linear, but doesn't require shifting). Tombstones aren't implemented for this first version.
Additional Notes
Deduplication is performed at serialization time. We initially set up for sort + deduplication to avoid allocation, but this would require larger changes in libdatadog by requiring Span values to be
Ord. For now, deduplication is based on aHashSetsinceSpanData::Textis alreadyHash, but this incurs one allocation. I think it's reasonable because the serialization path is not hot anyway, and we can later requestOrdon values to go the in-placesort_deduproute instead if desired.How to test the change?
Run tests.