{"id":51126415,"url":"https://github.com/vnmakarov/ihtab","last_synced_at":"2026-06-25T08:01:41.982Z","repository":{"id":366848536,"uuid":"1242712587","full_name":"vnmakarov/ihtab","owner":"vnmakarov","description":"High performance C/C++ hash tables","archived":false,"fork":false,"pushed_at":"2026-06-23T14:01:27.000Z","size":3078,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-23T16:07:14.297Z","etag":null,"topics":["c","cpp","hashtable"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vnmakarov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-18T17:22:54.000Z","updated_at":"2026-06-23T14:03:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vnmakarov/ihtab","commit_stats":null,"previous_names":["vnmakarov/ihtab"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/vnmakarov/ihtab","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vnmakarov%2Fihtab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vnmakarov%2Fihtab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vnmakarov%2Fihtab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vnmakarov%2Fihtab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vnmakarov","download_url":"https://codeload.github.com/vnmakarov/ihtab/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vnmakarov%2Fihtab/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34765322,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","cpp","hashtable"],"created_at":"2026-06-25T08:01:41.094Z","updated_at":"2026-06-25T08:01:41.976Z","avatar_url":"https://github.com/vnmakarov.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"![ixht](ixht.png)\n\n# ihtab / ixhtab — single-header indexed hash tables\n\nTwo open-addressing hash tables with separate element storage, available\nas C++20 templates (`ihtab.hpp`, `ixhtab.hpp`) and C11 macro-generated\nheaders (`ihtab.h`, `ixhtab.h`).  No build system required to use them.\n\n## Design\n\nThese days hash tables based on [swiss\ntable](https://abseil.io/about/design/swisstables) design have become\nvery popular.  Standard swiss table is a **direct** open-address hash\ntable that stores key-value pairs with probing data in one array.  To\ndecrease memory waste on unused slots for key-value pairs, swiss table\nuses a very high load factor (maximum used key-value pairs / allocated\nkey-value pairs).  This causes frequent cache misses and branch\nmispredictions on failed lookups.  Swiss table keeps probing data in\ngroups and uses SIMD to speed up the search.  Within each group the\nprobing is linear.\n\nBoth tables here take a path different from the swiss table: a compact\narray of 8-bit tags (7-bit hash fingerprint + 1 empty/deleted bit) is\nprobed with SIMD; the actual elements live in a **separate dense\narray** and are referenced by small integer indices.  For linear\nprobing at 50 % load the expected average probe count is ~1 probe vs\n~4 for a 7/8-loaded Swiss table for successful search and ~2 vs 32 for\nunsuccessful search.  Iteration walks the dense element array with a\ndeleted-bit check — no empty-slot gaps.\n\n### ihtab\n\n- Single contiguous element array, 32-bit slot indices.\n- 50 % load factor; rebuilds the whole table when full.\n- Fastest average lookup; suits general-purpose use.\n\n### ixhtab\n\n- [Extendible hash table](https://en.wikipedia.org/wiki/Extendible_hashing)\n- Directory of bins, each bin is an ihtab with 16-bit indices (max 2^15\n  elements per bin).\n- Only the full bin is split on overflow; the rest of the table is\n  untouched.\n- Trades a directory indirection for bounded rebuild latency; suits large\n  tables with latency-sensitive workloads.\n\nMore information about the design can be found in this [blog post](https://vnmakarov.github.io/data%20structures/c/c++/open-source/2026/06/23/two-indexed-hash-tables.html).\n\n## C++ Usage\n\nBoth C++ tables follow the same API.  Define an entry type that bundles key\nand value, plus a hash functor and equality functor, then instantiate.\n\n```cpp\n#include \"ihtab.hpp\"\nusing namespace iht;\n\nstruct Entry { uint64_t key; int value; };\nstruct Hash  { hash_t operator()(const Entry \u0026e) const { return e.key * 11400714819323198485ULL; } };\nstruct Eq    { bool operator()(const Entry \u0026a, const Entry \u0026b) const { return a.key == b.key; } };\n\nusing Tab = ihtab\u003cEntry, Hash, Eq\u003e;\n\nTab t(64);  // min_size=64; default is 8\n\n// Insert\nEntry e{42, 100};\nEntry *slot;\nif (!t.perform(e, INSERT, \u0026slot)) *slot = e; // new element — write it\n\n// Lookup\nEntry key{42, 0};\nif (t.perform(key, FIND, \u0026slot)) printf(\"%d\\n\", slot-\u003evalue);\n\n// Delete\nt.perform(key, DELETE, \u0026slot);\n\n// Iterate\nfor (auto \u0026e : t) printf(\"%llu -\u003e %d\\n\", e.key, e.value);\n```\n\nFor ixhtab, use `#include \"ixhtab.hpp\"` with `using namespace ixht;` and\nreplace `ihtab` with `ixhtab`.\n\n`perform` returns `true` when the element was found (FIND/DELETE) or already\nexisted (INSERT), `false` on a new insertion.  On `false` from\nINSERT, `*slot` points to uninitialised memory — you must write the element.\n\n## C Usage\n\nInclude the header and invoke the `DEFINE_IHT(El, Hash, Eq)` macro to\ngenerate type-specific structs and functions with an `_El` suffix.\n`Hash` and `Eq` are ordinary function names.\n\n```c\n#include \"ihtab.h\"\n\ntypedef struct { uint64_t key; int value; } entry;\n\nstatic inline iht_hash_t entry_hash(entry e) { return e.key * 11400714819323198485ULL; }\nstatic inline bool entry_eq(entry a, entry b) { return a.key == b.key; }\n\nDEFINE_IHT(entry, entry_hash, entry_eq)\n\n// Creates: struct iht_entry, iht_create_entry, iht_perform_entry, etc.\n\nstruct iht_entry t;\niht_create_entry(\u0026t, 64);\n\n// Insert\nentry e = {42, 100};\nentry *slot;\nif (!iht_perform_entry(\u0026t, \u0026e, IHT_INSERT, \u0026slot)) *slot = e;\n\n// Lookup\nentry key = {42, 0};\nif (iht_perform_entry(\u0026t, \u0026key, IHT_FIND, \u0026slot)) printf(\"%d\\n\", slot-\u003evalue);\n\n// Delete\niht_perform_entry(\u0026t, \u0026key, IHT_DELETE, \u0026slot);\n\n// Iterate\nstruct iht_iter_entry it = iht_iter_begin_entry(\u0026t);\nwhile (iht_iter_valid_entry(\u0026it)) {\n    printf(\"%llu -\u003e %d\\n\", it.ptr-\u003ekey, it.ptr-\u003evalue);\n    iht_iter_next_entry(\u0026t, \u0026it);\n}\n\niht_destroy_entry(\u0026t);\n```\n\nReplace `iht` / `IHT_*` / `DEFINE_IHT` with `ixht` / `IXHT_*` /\n`DEFINE_IXHT` to use ixhtab.\n\n## Requirements\n\nC++20 or C11 compiler.  x86/x86-64 uses SSE2; AArch64 uses NEON; other\narchitectures fall back to portable SWAR bit tricks.\n\n## Install\n\n```sh\nmake install            # copies all headers to /usr/local/include\nmake install PREFIX=~/.local\nmake uninstall\n```\n\n## Test\n\n```sh\nmake test               # build and run correctness tests\n```\n\nTests cover insert, find, replace, delete, duplicate-key handling,\niteration (both C++ forward iterators and lightweight iterators), and\nlarge tables (50 000 elements with deletions triggering rebuilds/splits).\n\n## Benchmark\n\n```sh\nmake bench              # build and run vs absl::flat_hash_map\n```\n\nBenchmarks run against `absl::flat_hash_map` with the vmum hash on\nsizes Small (100), Medium (10 000), Large (1 000 000).\n\nGeometric mean on AMD 9900x across all benchmarks (lower is better):\n\n| Implementation | ns/op | vs absl |\n|----------------|------:|--------:|\n| absl           |   7.0 |  1.000x |\n| C++ ixhtab     |   6.4 |  0.911x |\n| C++ ihtab      |   4.4 |  0.628x |\n| C ixhtab       |   6.3 |  0.897x |\n| C ihtab        |   4.1 |  0.587x |\n\nGeometric mean on Intel 270K+ across all benchmarks:\n\n| Implementation | ns/op | vs absl |\n|----------------|------:|--------:|\n| absl           |   8.5 |  1.000x |\n| C++ ixhtab     |   7.3 |  0.863x |\n| C++ ihtab      |   5.1 |  0.603x |\n| C ixhtab       |   7.2 |  0.843x |\n| C ihtab        |   4.9 |  0.576x |\n\nGeometric mean on Apple M4 across all benchmarks:\n\n| Implementation | ns/op | vs absl |\n|----------------|------:|--------:|\n| absl           |   5.9 |  1.000x |\n| C++ ixhtab     |   6.4 |  1.080x |\n| C++ ihtab      |   3.7 |  0.631x |\n| C ixhtab       |   5.3 |  0.889x |\n| C ihtab        |   3.6 |  0.601x |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvnmakarov%2Fihtab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvnmakarov%2Fihtab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvnmakarov%2Fihtab/lists"}