axhash-map
High-performance HashMap and HashSet collections for Rust.
Powered by hashbrown (SwissTable layout) · Fueled by axhash (AES-NI accelerated hashing)
Why axhash-map?
std::collections::HashMap uses SipHash-1-3 by default — a secure but comparatively slow hash function. For most workloads you don't need cryptographic resistance; you need throughput.
axhash-map swaps the hasher for axhash, which exploits hardware AES instructions (AES-NI on x86-64, AES on ARMv8) to produce hashes at near-memory-bandwidth speed. The underlying table is hashbrown (SwissTable), the same implementation that backs std::collections::HashMap in Rust's standard library — so the table operations are identical; only the hashing step is faster.
┌─────────────────────────────────────────┐
│ axhash-map │
│ │
│ AxHashMap<K, V> AxHashSet<T> │
│ │ │ │
│ hashbrown::HashMap hashbrown::HashSet│
│ (SwissTable layout) │
│ │ │ │
│ axhash::AxBuildHasher │
│ (AES-NI accelerated hash engine) │
└─────────────────────────────────────────┘
Benchmark Results
Measured on Apple Silicon (release build, N = 100,000 items).
Hasher comparison:
- AxHashMap (
hashbrown+axhash) - std::collections::HashMap (
SipHash-1-3)
| Scenario | AxHashMap | std HashMap | Speedup |
|---|---|---|---|
Insert — u64 keys |
379 µs | 1,032 µs | 2.7× |
Insert — String keys |
896 µs | 1,673 µs | 1.9× |
| Lookup — all hits | 200 µs | 748 µs | 3.7× |
| Lookup — 50% hit / 50% miss | 767 µs | 1,994 µs | 2.6× |
| Iteration (full scan) | 130 µs | 124 µs | ~equal |
Iteration performance is effectively identical because iteration does not invoke the hasher. Both maps use the same SwissTable layout provided by
hashbrown.
Run the benchmarks yourself:
# HTML reports → target/criterion/
Installation
# Cargo.toml
[]
= "0.1"
No feature flags required. The AES acceleration is detected at runtime; a portable fallback is used automatically on CPUs without AES instructions.
Quick start
HashMap
use AxHashMap;
// Create an empty map.
let mut scores: = new;
scores.insert;
scores.insert;
scores.insert;
// Index operator
println!; // 42
// Safe lookup
if let Some = scores.get
// Iteration
for in &scores
HashSet
use AxHashSet;
let mut seen: = new;
seen.insert;
seen.insert;
seen.insert; // duplicate — ignored
assert_eq!;
assert!;
// Set operations (via Deref to hashbrown::HashSet)
let a: = .into_iter.collect;
let b: = .into_iter.collect;
let union: = a.union.copied.collect;
let intersection: = a.intersection.copied.collect;
Constructors
Both AxHashMap and AxHashSet expose the same constructor family:
use ;
// Default seed (fastest, deterministic within a process)
let map: = new;
let set: = new;
// Pre-allocate to avoid rehashing
let map = with_capacity;
let set = with_capacity;
// Custom seed — use a random value for hash-flooding resistance
let hasher = with_seed;
let map: = with_hasher;
// Custom seed + pre-allocated capacity
let hasher = with_seed;
let map: = with_capacity_and_hasher;
Collecting from iterators
use ;
// FromIterator for AxHashMap
let map: = vec!
.into_iter
.collect;
// FromIterator for AxHashSet
let set: = .into_iter.collect; // len == 3
// Extend
let mut map: = new;
map.extend;
map.extend;
All hashbrown methods are available
AxHashMap and AxHashSet implement Deref / DerefMut to the underlying
hashbrown::HashMap / hashbrown::HashSet, so every method on those types —
entry, retain, drain, reserve, shrink_to_fit, raw_entry, and more —
is directly accessible without any extra imports.
use AxHashMap;
let mut map: = new;
// entry API (from hashbrown, via Deref)
map.entry.and_modify.or_insert;
map.entry.and_modify.or_insert;
assert_eq!;
// retain
map.insert;
map.retain;
assert!;
Interoperability with raw hashbrown types
The crate re-exports RawHashMap and RawHashSet (the bare hashbrown types)
and provides From conversions in both directions so you can cross the boundary
without a direct hashbrown dependency in your own Cargo.toml.
use ;
// Wrap a raw hashbrown map
let raw: =
with_hasher;
let wrapped: = raw.into;
// Unwrap back to hashbrown
let raw: = wrapped.into_inner;
When to use a custom seed
The default AxBuildHasher::new() uses an internal constant as seed — the
output is deterministic across runs. This is fine for most workloads.
If your map accepts keys from untrusted external input (e.g. HTTP request parameters) and you want to defend against hash-flooding attacks, supply a random seed derived from OS entropy:
use ;
// In real code, generate this from `rand`, `getrandom`, or similar.
let seed: u64 = /* os_random_u64() */ 0x1234_5678_9abc_def0;
let mut map: =
with_hasher;
Feature flags
This crate has no feature flags of its own. The AES hardware path in axhash is selected at runtime via CPUID — you always ship a single binary.
Dependency footprint
axhash-map
├── axhash-core (AxBuildHasher — AES hash engine)
└── hashbrown (SwissTable, no default features — ahash excluded)
axhash itself is not a dependency; axhash-core is the crate that owns
AxBuildHasher. The wrapper crate adds no utility functions we don't use.
License
MIT — see LICENSE.