compact-dict
compact-dict is a highly customizable, open-addressing dictionary in Rust. It features linear probing and continuous memory layout for string keys, heavily inspired by Mojo's Dict.
Features
- Customizable Layout: Generic integer types for key-count (
KC) and key-offset (KO) sizes. This flexibility allows you to perfectly balance memory usage (e.g.u16vsu32indices) depending on your scale requirements. - Cache Friendly: Linear probing design with optional cached hashes (
CACHING_HASHES). - SIMD Preparation: Includes preparations for SIMD-accelerated probing (via
portable_simd). - Configurable Mutability: Optional destructive operations via the
DESTRUCTIVEconstant generic parameter, which toggles bitsets to mask deleted operations cleanly payload. - Performant Hash Function: By default, it uses high-performance internal hasher variants built upon aHash (like
MojoAHashStrHash).
Requirements
- Rust Nightly: The crate strictly uses
#![feature(portable_simd)]and relies on thestd::simdfeatures, meaning you must compile it with the nightly Rust toolchain.
Usage
Below is a standard usage example:
use Dict;
Running Tests
To test the package, run cargo test utilizing the nightly toolchain.
Benchmarking:
There are two benchmarks in the repository: workload_bench (simulating real-world string ingestion and mutations) and dict_bench (using Criterion for pure primitive operations like get).
RUSTFLAGS="-C target-cpu=native"
Workload Benchmark Results (workload_bench - Insertions + Mutations):
compact_dict_fx: 0.152 s ๐๐ฅ
fxhash: 0.182 s
std_hashmap: 0.267 s
hashbrown: 0.272 s
Pure Lookup Results (dict_bench - 10k random get operations):
RUSTFLAGS="-C target-cpu=native"
hashbrown: ~74 ยตs ๐๐ฅ
compact_dict: ~143 ยตs
Note: For the most accurate comparisons without allocation overhead skewing metrics, ensure to run tests natively with LTO enabled, as seen in the bench profile.
Performance Analysis & Honest Comparison
compact-dict is exceptionally fast for very specific workloads, beating out highly optimized SwissTable implementations like hashbrown and standard std::collections::HashMap. However, it makes severe trade-offs to achieve this speed. Here is an honest guide on when to use what:
โ
Where compact-dict Wins (Strengths)
- String-Heavy Initialization & Iteration: Instead of individually heap-allocating every
Stringkey like standard HashMaps do,compact-dictcopies all strings into a single densely-packed, continuousVec<u8>memory buffer (KeysContainer). - SIMD Vectorization: Lookups leverage
#![feature(portable_simd)]to compare up to 16 cachedu32hashes in exactly a single hardware instruction cycle. - Data Analysis & Short-lived Workloads: If you need to ingest millions of unique strings rapidly, perform some math/updates on their values, and then drop the map,
compact-dictwill significantly outpace its competitors.
โ Where compact-dict Loses (Weaknesses / Use hashbrown instead)
- Continuous Server Deletions: Deleting elements in
compact-dictonly marks a bit field as deleted (tombstoning). It never compacts or frees the physical string memory from the keys buffer. If you implement a long-running web server that constantly adds and removes strings,compact-dictwill act as a memory leak until the entire dictionary is dropped. - Generic Keys: The dictionary is hardcoded around
KeysContaineroffsets, heavily specializing in&str. You cannot drop inHashMap<Uuid, usize>or custom structs as keys easily. Standard map implementations are completely generic. - Ecosystem Stability: It relies on Nightly Rust explicitly for
std::simd.hashbrownhas zero unstable dependencies and runs practically everywhere perfectly optimized for all target architectures. - Pure Lookup Speed: In pure read-heavy workloads (e.g., retrieving 10,000 strings without inserting or scanning new ones), highly optimized SwissTables like
hashbrownstill outperformcompact-dict. As seen in the pure-getmicrobenchmark,hashbrowncan be about 2x faster thancompact-dictfor isolated random-access lookups. The performance strength ofcompact-dictrevolves around the combined speed of contiguous string ingestion, sequential iteration cache-locality, and mutation operations.