Where the numbers come from. Every figure on this page is a measurement, not an estimate: all eight maps interleaved in one process, given the same hash, running the fifteen workloads in unordered_dense/scripts/ab. Interleaving is what makes the comparison fair — machine drift lands on every candidate equally instead of on whichever ran last — and one hash for all of them is what keeps the subject the container rather than the hash function.
Measured with
- ankerl::unordered_dense
- v4.11.0
- Boost.Unordered
- 1.92.0 — supplies
unordered_flat_map, unordered_node_map and unordered_map. Re-running against 1.90.0 moved every one of them by less than the run-to-run noise, so these numbers are not sensitive to the Boost release.
- Abseil
- 20260107.1 — supplies
flat_hash_map and node_hash_map. The only candidate that is not header-only.
- tsl::robin_map
- 1.4.0
- libstdc++
- 16 (GCC 16.2.1) — this is whose
std::unordered_map was measured, and another standard library would give different numbers
- Compiler
- clang 22.1.8,
-O3 -DNDEBUG -std=c++17, no -march
- Machine
- AMD Ryzen 9 7950X, Linux
- Measured
- 4 September 2026
A summary number is a choice of basket, so here is the choice. These fifteen workloads come from unordered_dense's own benchmark suite, and two of them iterate — which is the thing it wins by the widest margin. Drop those two and the ranking changes hands:
- All fifteen workloads
unordered_dense 1.00 · boost-flat 1.21 · absl-flat 1.31 · boost-node 1.51 · absl-node 1.57 · tsl 1.79 · boost::unordered_map 2.09 · std 2.72
- The thirteen that do not iterate
- boost-flat 0.91 ·
unordered_dense 1.00 · absl-flat 1.04 · boost-node 1.17 · absl-node 1.27 · tsl 1.33 · boost::unordered_map 1.72 · std 2.14
Counting first places rather than averaging says the same thing more bluntly: boost::unordered_flat_map is quickest on eight of the fifteen, unordered_dense on four, tsl::robin_map on two and absl::flat_hash_map on one. Neither view is the honest one on its own, which is why the quiz asks what your code does instead of handing you a league table.
Three things that were checked rather than assumed. All eight produce byte-identical results on every workload, so they are demonstrably doing the same work. Boost recognises the shared hash as avalanching and therefore skips its own mixing step, so sharing it does not tax anyone — boost::hash is not avalanching, so this is if anything generous to Boost. And every container keeps its own default load factor, which is part of what is being compared: at a million entries they sit anywhere between 0.48 and 0.69 full.
Memory was measured separately. One map per process, so no peak can contaminate another: build a million entries from empty, then read the high-water mark and the settled size out of /proc/self/status. That run deliberately leaves the allocator alone — the timing runs raise glibc's mmap and trim thresholds to keep pages out of the kernel, which is right for a stopwatch and would falsify a memory figure.
Your workload is not those workloads. This quiz narrows eight choices to one worth trying first; it does not replace measuring the thing you actually built. If the difference matters enough to ask the question, it matters enough to benchmark.