[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-81806":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":12,"openIssues":13,"contributorsCount":13,"subscribersCount":13,"size":13,"stars1d":13,"stars7d":13,"stars30d":13,"stars90d":13,"forks30d":13,"starsTrendScore":13,"compositeScore":14,"rankGlobal":9,"rankLanguage":9,"license":15,"archived":16,"fork":16,"defaultBranch":17,"hasWiki":18,"hasPages":16,"topics":19,"createdAt":9,"pushedAt":9,"updatedAt":20,"readmeContent":21,"aiSummary":22,"trendingCount":13,"starSnapshotCount":13,"syncStatus":23,"lastSyncTime":24,"discoverSource":25},81806,"probemap","NikoMalik\u002Fprobemap","NikoMalik","simd swiss table based map",null,"Rust",36,1,0,40.9,"Apache License 2.0",false,"main",true,[],"2026-06-12 04:01:35","# probemap\n\nA SwissTable hash map with direct SSE2 SIMD — works on **stable Rust**.\n\n## Features\n\n- **Direct SSE2 intrinsics** on x86_64, **scalar fallback** on other architectures\n- **`KeyExtract` trait** — store only values when the key is embedded in the value\n- **Pluggable `BuildHasher`** — default `AutoHasher` (FxHash for ints, buthash for byte slices), swap for any hasher\n- **Custom allocator support** via `allocator-api2` (stable polyfill for the Allocator API)\n- **Zero-cost empty table** — `new()` does not allocate; first `insert` triggers allocation\n- **SIMD-accelerated iterator** — scans 16 control bytes per group via bitmask\n- **Bulk `memcpy` clone** for `Copy` types\n- **87% load factor** with linear group probing\n\n## Usage\n\n```rust\nuse probemap::{KeyExtract, SwissTable, PairExtract};\n\n\u002F\u002F Simple (K, V) usage — like HashMap\u003CK, V>\ntype MyMap = SwissTable\u003CPairExtract\u003Cu64, String>>;\nlet mut map = MyMap::new();\nmap.insert((42, \"hello\".to_string()));\nassert_eq!(map.get(&42).unwrap().1, \"hello\");\n\n\u002F\u002F key_from_value pattern — key embedded in value\nstruct User { id: u64, name: String }\n\nstruct UserById;\nimpl KeyExtract for UserById {\n    type Key = u64;\n    type Value = User;\n    fn extract(value: &User) -> &u64 { &value.id }\n}\n\nlet mut users = SwissTable::\u003CUserById>::with_capacity(64);\nusers.insert(User { id: 1, name: \"Alice\".into() });\nassert_eq!(users.get(&1).unwrap().name, \"Alice\");\n```\n\n### Custom hasher\n\n```rust\nuse probemap::{SwissTable, PairExtract, FxBuildHasher};\nuse std::collections::hash_map::RandomState;\n\n\u002F\u002F Use FxHash only (no buthash)\nlet map = SwissTable::\u003CPairExtract\u003Cu64, u64>, FxBuildHasher>::with_hasher(FxBuildHasher);\n\n\u002F\u002F Use SipHash (DoS-resistant)\nlet map = SwissTable::\u003CPairExtract\u003CString, u64>, RandomState>::with_hasher(RandomState::new());\n```\n\n## API\n\n| Method | Description |\n|---|---|\n| `new()` \u002F `new_in(alloc)` | Empty table (no allocation) |\n| `with_capacity(n)` \u002F `with_capacity_in(n, alloc)` | Pre-allocate for `n` elements |\n| `with_hasher(S)` | Empty table with custom hash builder |\n| `with_capacity_and_hasher(n, S)` | Pre-allocate with custom hash builder |\n| `with_hasher_and_alloc(S, A)` | Custom hash builder + custom allocator |\n| `insert(value)` | Insert or update by key |\n| `get(key)` \u002F `get_mut(key)` | Lookup by key |\n| `remove(key)` | Remove by key (returns `bool`) |\n| `contains(key)` | Key existence check |\n| `get_or_insert(value)` | Get existing or insert new |\n| `clear()` | Drop all values, keep allocation |\n| `clone_table()` \u002F `clone()` | Deep copy |\n| `iter()` | Iterator over all values |\n| `prefetch(key)` | Hint CPU to preload cache lines |\n| `hasher()` | Reference to the hash builder |\n| `allocator()` | Reference to the allocator |\n| `len()` \u002F `is_empty()` \u002F `capacity()` | Size queries |\n\n## Benchmarks (vs hashbrown+fxhash, n=1000)\n\n| Benchmark | probemap | hb+fxhash | hb+foldhash | vs hb+fxhash |\n|---|---|---|---|---|\n| insert (random) | **6.03 µs** | 6.51 µs | 6.89 µs | **-7%** |\n| insert (serial) | **4.55 µs** | 5.90 µs | 6.64 µs | **-23%** |\n| grow_insert | **19.8 µs** | 21.7 µs | 27.3 µs | **-9%** |\n| lookup (random) | **2.83 µs** | 4.11 µs | 4.47 µs | **-31%** |\n| lookup (serial) | **2.73 µs** | 4.11 µs | 4.58 µs | **-34%** |\n| lookup_fail | **2.71 µs** | 3.17 µs | 3.54 µs | **-15%** |\n| insert_erase | **10.0 µs** | 15.4 µs | 16.0 µs | **-35%** |\n| iter | **1.21 µs** | 1.25 µs | 1.25 µs | **-3%** |\n| clone | **1.19 µs** | 1.25 µs | 1.29 µs | **-5%** |\n\n\n\n## Why faster than hashbrown?\n\nBoth probemap and hashbrown implement the same SwissTable algorithm and use the\nsame SSE2 intrinsics. The performance difference comes from how the surrounding\ncode is structured.\n\n**Monomorphized key comparison vs `dyn FnMut`.**\nhashbrown's inner lookup (`find_inner`) takes the key comparator as\n`&mut dyn FnMut(usize) -> bool` — a trait object. This means every key\ncomparison in the probe loop is an indirect call through a vtable pointer.\nThe CPU cannot predict it, and LLVM cannot inline through it. This is a\ndeliberate trade-off in hashbrown to reduce binary size from monomorphization.\nprobemap calls `E::extract(slot) == key` directly — the compiler monomorphizes\nthe comparison for each concrete type and inlines it into the probe loop,\neliminating the indirect call overhead entirely. This is likely the single\nbiggest contributor to the ~30% lookup speedup.\n\n**Forward slot layout.**\nhashbrown stores slots in reverse order relative to control bytes\n(`base.sub(index)`). probemap uses forward order (`slots.add(index)`). Modern\nCPUs prefetch forward; reverse indexing works against the hardware prefetcher.\n\n**Flatter data path.**\nhashbrown has four layers: `HashMap` -> `HashTable` -> `RawTable` ->\n`RawTableInner`. `RawTableInner` type-erases the value to `*u8` and reconstructs\n`Bucket\u003CT>` through pointer arithmetic. probemap is a single struct with inline\nmethods — no type erasure, no `Bucket` wrapper, no intermediate layers.\n\n**Linear group probing.**\nhashbrown uses triangular probing (`stride += 16; pos += stride`) — two\nadditions and an extra field per step. probemap uses linear probing with\nmirrored control bytes: `pos = (pos + 16) & mask` — one add, one and. The\nmirror (first 16 control bytes duplicated at the end of the array) lets\nunaligned SIMD loads wrap around without sentinel bytes or branches.\n\n**`insert_assume_unique` during grow.**\nWhen rehashing into a fresh table, there can be no duplicates. probemap skips\nthe h2 match + key comparison loop and only scans for empty slots. hashbrown\nalso optimizes this path but carries extra bookkeeping (`growth_left` updates)\nin the hot loop.\n\n**SIMD iterator with `match_full()`.**\nThe iterator loads 16 control bytes, extracts a bitmask of occupied slots with\n`_mm_movemask_epi8`, and pops bits with `trailing_zeros`. Empty groups are\nskipped in one branch. The struct is flat — `(table, group_pos, bitmask)`.\n\n**Bulk `memcpy` clone.**\n`clone_table()` checks `needs_drop::\u003CV>()` at compile time. For `Copy` types\nthe entire slots array is a single `memcpy` — no per-element loop, no rehash.\n\n## Requirements\n\n- **Rust edition 2024** (1.85+)\n- Stable toolchain (no nightly features required)\n- SSE2 SIMD on x86_64, scalar fallback on other architectures\n\n## License\nApache License\n","probemap 是一个基于 SwissTable 的哈希映射库，利用 SIMD 指令集（特别是 SSE2）进行加速。其核心功能包括直接使用 SSE2 内在函数提高 x86_64 架构上的性能，在其他架构上则回退到标量运算；支持通过 `KeyExtract` 特性存储嵌入键的值以节省空间；提供可插拔的 `BuildHasher` 机制，默认使用 FxHash 和 buthash，并允许用户自定义哈希器；同时兼容自定义内存分配器。此外，该库还具备零成本空表、SIMD 加速迭代器等特性。probemap 适用于需要高性能哈希表且对内存使用效率有较高要求的应用场景，如实时数据处理系统或大规模数据存储解决方案。",2,"2026-06-11 04:06:48","CREATED_QUERY"]