How Abseil Swiss Table Containers Compare to `std::unordered_map`: Performance and Design Analysis
Abseil Swiss table containers store key-value pairs in contiguous memory arrays, delivering superior cache locality and iteration speed compared to node-based std::unordered_map, though they sacrifice strong exception safety and pointer stability for these performance gains.
The Abseil C++ library (abseil/abseil-cpp) provides a family of high-performance associative containers built on the Swiss table design. These flat_hash_map and flat_hash_set types offer a direct alternative to standard library containers, prioritizing speed and memory efficiency through flat, cache-friendly storage layouts implemented in absl/container/flat_hash_map.h and related headers.
Memory Layout and Cache Efficiency
Swiss table containers utilize a flat array storage model where key-value pairs reside contiguously in memory. This design eliminates the per-node allocation overhead found in std::unordered_map, significantly improving cache locality and reducing overall memory footprint.
In contrast, std::unordered_map employs a node-based bucket list where each element requires a separate heap allocation and pointer indirection. This structure scatters data across memory, causing cache misses during traversal. The core Swiss table algorithm powering this layout is implemented in absl/container/internal/raw_hash_map.h, which manages the dense array packing and metadata handling.
Performance Characteristics
Iteration Speed: Iterating over absl::flat_hash_map costs O(capacity) because elements are densely packed, enabling predictable, cache-friendly traversal. Conversely, iterating std::unordered_map requires walking linked nodes, which scales with element count and suffers from pointer chasing latency.
Insertion and Deletion: Both containers offer O(1) amortized insertion and lookup. However, flat_hash_map moves the entire array during rehash operations rather than reallocating individual nodes. According to the header comments in absl/container/flat_hash_map.h (lines 16-20), deletion may leave "deleted" slots that are reused, though heavy deletion can degrade performance until the next rehash.
API Design and Advanced Features
Reservation by Element Count: Unlike std::unordered_map, which accepts an opaque bucket count, Abseil constructors take a reservation size representing the expected element count. This approach, documented in absl/container/flat_hash_map.h (lines 74-76), provides a clearer API for typical use cases by decoupling user intent from internal bucket management.
Heterogeneous Lookup: Swiss tables support transparent hashing out-of-the-box. By providing a custom Hash/Eq with is_transparent, users can perform lookups with different key types without constructing temporary objects (see lines 64-68 in flat_hash_map.h). This functionality relies on absl/container/internal/hash_policy_traits.h for type resolution. Standard library containers lack this feature without custom wrappers.
Load Factor Management: The container manages its own load factor internally; calling max_load_factor() has no effect on behavior, existing only for API compatibility (lines 70-77 in flat_hash_map.h). This automatic tuning optimizes memory usage versus performance without manual intervention.
Stability and Safety Trade-offs
Exception Safety: flat_hash_map is not exception-safe. If an exception occurs during insertion, the container may be left in an unspecified state, as noted at the top of absl/container/flat_hash_map.h. Standard library maps provide the strong exception guarantee for most operations.
Pointer Stability: Because elements are stored contiguously and moved on rehash, pointers to elements in flat_hash_map become invalid after rehashing. For use cases requiring pointer stability, Abseil provides node_hash_map, which keeps nodes separate while retaining the same API surface. This matches the node-based stability of std::unordered_map.
Utility Functions: Abseil provides optimized utilities such as erase_if and c_for_each_fast, along with a custom swap implementation that avoids accidental std::swap invocation (lines 96-101 in flat_hash_map.h). These helpers leverage the absl::Hash framework defined in absl/hash/hash.h for consistent hashing behavior.
Practical Code Example
#include "absl/container/flat_hash_map.h"
#include <unordered_map>
#include <string>
#include <iostream>
int main() {
// Abseil flat_hash_map – fast, cache‑friendly
absl::flat_hash_map<std::string, int> absl_map{
{"alice", 30},
{"bob", 25},
};
// Heterogeneous lookup: search with a string_view without converting
std::string_view key = "bob";
if (auto it = absl_map.find(key); it != absl_map.end()) {
std::cout << "Abseil: " << it->first << " -> " << it->second << '\n';
}
// std::unordered_map – node‑based, pointer stable
std::unordered_map<std::string, int> std_map{
{"alice", 30},
{"bob", 25},
};
// No heterogeneous lookup; need to construct a string
if (auto it = std_map.find(std::string(key)); it != std_map.end()) {
std::cout << "STD: " << it->first << " -> " << it->second << '\n';
}
// Demonstrate reservation (Abseil) vs bucket count (STD)
absl_map.reserve(1000); // ensures space for 1000 elements without rehash
std_map.rehash(1000); // sets bucket count; actual capacity may differ
return 0;
}
Summary
- Abseil Swiss table containers (
flat_hash_map,flat_hash_set) utilize contiguous memory storage inabsl/container/internal/raw_hash_map.h, eliminating per-node allocation overhead and maximizing cache locality. - Iteration performance scales with capacity rather than element count, enabling faster traversal than the linked-node structure of
std::unordered_map. - Heterogeneous lookup and reservation by element count provide ergonomic APIs absent from standard library containers.
- Trade-offs include lack of exception safety and pointer instability on rehash, mitigated by the
node_hash_mapalternative when stability is required. - Load factor is managed internally and cannot be customized, simplifying usage while optimizing performance.
Frequently Asked Questions
When should I use flat_hash_map versus node_hash_map?
Choose flat_hash_map when you prioritize raw performance and cache efficiency in latency-critical workloads such as game engines or networking stacks. Opt for node_hash_map when you require pointer stability across insertions, as it preserves node addresses similarly to std::unordered_map while maintaining the Abseil API.
How does Abseil handle deleted elements in flat_hash_map?
Deletion marks slots as "deleted" rather than immediately compacting the array. According to the warning in absl/container/flat_hash_map.h (lines 16-20), excessive deletion can degrade performance by creating sparse regions until the next rehash occurs. For workloads with heavy deletion patterns, periodic rehashing or using node_hash_map may be preferable.
Can I use flat_hash_map as a drop-in replacement for std::unordered_map?
While the APIs are similar, flat_hash_map is not a complete drop-in replacement due to its lack of exception safety and pointer stability. Code relying on stable references during rehashing or strong exception guarantees will require modification, potentially using node_hash_map instead for compatibility.
Does flat_hash_map support custom hash functions?
Yes, flat_hash_map accepts custom hash and equality functors. It integrates with absl::Hash from absl/hash/hash.h by default, and supports transparent hashing via is_transparent traits defined in absl/container/internal/hash_policy_traits.h, enabling lookups with heterogeneous key types without conversion.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →