{"id":13502997,"url":"https://github.com/jonhoo/griddle","last_synced_at":"2025-05-15T09:07:57.258Z","repository":{"id":39594781,"uuid":"275959529","full_name":"jonhoo/griddle","owner":"jonhoo","description":"A HashMap variant that spreads resize load across inserts","archived":false,"fork":false,"pushed_at":"2025-01-28T03:47:05.000Z","size":239,"stargazers_count":192,"open_issues_count":4,"forks_count":7,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-05-15T01:49:08.605Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonhoo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","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}},"created_at":"2020-06-30T00:49:13.000Z","updated_at":"2025-05-13T21:27:09.000Z","dependencies_parsed_at":"2024-10-25T18:45:49.080Z","dependency_job_id":"4720732b-63d1-46d1-a1f2-34f0c280e071","html_url":"https://github.com/jonhoo/griddle","commit_stats":{"total_commits":163,"total_committers":19,"mean_commits":8.578947368421053,"dds":"0.14723926380368102","last_synced_commit":"404ca4ee45bc6130cbf86d9c61c87ad241177055"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonhoo%2Fgriddle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonhoo%2Fgriddle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonhoo%2Fgriddle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonhoo%2Fgriddle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonhoo","download_url":"https://codeload.github.com/jonhoo/griddle/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254259314,"owners_count":22040810,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-07-31T22:02:32.983Z","updated_at":"2025-05-15T09:07:52.248Z","avatar_url":"https://github.com/jonhoo.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"[![Crates.io](https://img.shields.io/crates/v/griddle.svg)](https://crates.io/crates/griddle)\n[![Documentation](https://docs.rs/griddle/badge.svg)](https://docs.rs/griddle/)\n[![codecov](https://codecov.io/gh/jonhoo/griddle/graph/badge.svg?token=D9aE15xWkz)](https://codecov.io/gh/jonhoo/griddle)\n![Maintenance](https://img.shields.io/badge/maintenance-experimental-blue.svg)\n\nA `HashMap` variant that spreads resize load across inserts.\n\nMost hash table implementations (including [`hashbrown`], the one in\nRust's standard library) must occasionally \"resize\" the backing memory\nfor the map as the number of elements grows. This means allocating a new\ntable (usually of twice the size), and moving all the elements from the\nold table to the new one. As your table gets larger, this process takes\nlonger and longer.\n\nFor most applications, this behavior is fine — if some very small number\nof inserts take longer than others, the application won't even notice.\nAnd if the map is relatively small anyway, even those \"slow\" inserts are\nquite fast. Similarly, if your map grow for a while, and then _stops_\ngrowing, the \"steady state\" of your application won't see any resizing\npauses at all.\n\nWhere resizing becomes a problem is in applications that use maps to\nkeep ever-growing state where tail latency is important. At large scale,\nit is simply not okay for one map insert to take 30 milliseconds when\nmost take below a **micro**second. Worse yet, these resize pauses\ncan compound to create [significant spikes] in tail latency.\n\nThis crate implements a technique referred to as \"incremental resizing\",\nin contrast to the common \"all-at-once\" approached outlined above. At\nits core, the idea is pretty simple: instead of moving all the elements\nto the resized map immediately, move a couple each time an insert\nhappens. This spreads the cost of moving the elements so that _each_\ninsert becomes a little slower until the resize has finished, instead of\n_one_ insert becoming a _lot_ slower.\n\nThis approach isn't free, however. While the resize is going on, the old\ntable must be kept around (so memory isn't reclaimed immediately), and\nall reads must check both the old and new map, which makes them slower.\nOnly once the resize completes is the old table reclaimed and full read\nperformance restored.\n\nTo help you decide whether this implementation is right for you, here's\na handy reference for how this implementation compares to the standard\nlibrary map:\n\n - Inserts all take approximately the same time.\n   After a resize, they will be slower for a while, but only by a\n   relatively small factor.\n - Memory is not reclaimed immediately upon resize.\n - Reads and removals of **old** or **missing** keys are slower for a\n   while after a resize.\n - The incremental map is slightly larger on the stack.\n - The \"efficiency\" of the resize is slightly lower as the all-at-once\n   resize moves the items from the small table to the large one in\n   batch, whereas the incremental does a series of inserts.\n\n## Benchmarks\n\nThere is a silly, but illustrative benchmark in `benches/vroom.rs`. It\njust runs lots of inserts back-to-back, and measures how long each one\ntakes. The problem quickly becomes apparent:\n\n```console\n$ cargo bench --bench vroom \u003e vroom.dat\nhashbrown::HashMap max: 38.335088ms, mean: 94ns\ngriddle::HashMap max: 1.846561ms, mean: 126ns\n```\n\nYou can see that the standard library implementation (through\n`hashbrown`) has some pretty severe latency spikes. This is more readily\nvisible through a timeline latency plot (`misc/vroom.plt`):\n\n![latency spikes on resize](https://raw.githubusercontent.com/jonhoo/griddle/master/misc/vroom.png)\n\nResizes happen less frequently as the map grows, but they also take\nlonger _when_ they occur. With griddle, those spikes are mostly gone.\nThere is a small linear component left, which I believe comes from the\nwork required to find the buckets that hold elements that must be moved.\n\n## Implementation\n\nGriddle uses the\n[`hashbrown::raw`](https://docs.rs/hashbrown/0.14/hashbrown/raw/index.html)\nAPI, which allows it to take advantage of all the awesome work that has\ngone into making `hashbrown` as fast as it is. The key different parts\nof griddle live in `src/raw/mod.rs`. The `raw` API was [removed in\n`hashbrown` 0.15](https://github.com/rust-lang/hashbrown/issues/545), so\nGriddle is stuck on 0.14 for now.\n\nGriddle aims to stick as closely to `hashbrown` as it can, both in terms\nof code and API. `src/map.rs` and `src/set.rs` are virtually identical\nto the equivalent files in `hashbrown` (I encourage you to diff them!),\nwithout only some (currently;\n[#4](https://github.com/jonhoo/griddle/issues/4)) unsupported API\nremoved.\n\n## Why \"griddle\"?\n\nYou can amortize the cost of making hashbrowns by using a griddle..?\n\n[`hashbrown`]: https://crates.io/crates/hashbrown\n[significant spikes]: https://twitter.com/jonhoo/status/1277618908355313670\n\n## License\n\nLicensed under either of\n\n * Apache License, Version 2.0\n   ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)\n * MIT license\n   ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)\n\nat your option.\n\n## Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonhoo%2Fgriddle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonhoo%2Fgriddle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonhoo%2Fgriddle/lists"}