{"id":13690243,"url":"https://github.com/ronomon/hash-table","last_synced_at":"2025-04-06T06:08:09.228Z","repository":{"id":123905576,"uuid":"147699865","full_name":"ronomon/hash-table","owner":"ronomon","description":"Fast, reliable cuckoo hash table for Node.js.","archived":false,"fork":false,"pushed_at":"2021-10-18T11:26:15.000Z","size":47,"stargazers_count":303,"open_issues_count":1,"forks_count":13,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-30T05:08:37.663Z","etag":null,"topics":["bloom-filter","cache","cuckoo-hashing-algorithm","hashtable","lru","tabulation"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/ronomon.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}},"created_at":"2018-09-06T16:09:44.000Z","updated_at":"2025-02-14T16:56:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"9c2d1a11-caef-43e2-941e-5c865dcd69d4","html_url":"https://github.com/ronomon/hash-table","commit_stats":{"total_commits":24,"total_committers":2,"mean_commits":12.0,"dds":0.08333333333333337,"last_synced_commit":"b4f7e5f601088c2a1449a7c45ab70d4d0d20b2a8"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronomon%2Fhash-table","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronomon%2Fhash-table/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronomon%2Fhash-table/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronomon%2Fhash-table/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ronomon","download_url":"https://codeload.github.com/ronomon/hash-table/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247441051,"owners_count":20939239,"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":["bloom-filter","cache","cuckoo-hashing-algorithm","hashtable","lru","tabulation"],"created_at":"2024-08-02T16:00:50.258Z","updated_at":"2025-04-06T06:08:09.212Z","avatar_url":"https://github.com/ronomon.png","language":"JavaScript","readme":"# hash-table\n\nFast, reliable [cuckoo hash table](https://en.wikipedia.org/wiki/Cuckoo_hashing)\nfor Node.js.\n\n## Installation\n\n```\nnpm install @ronomon/hash-table\n```\n\n## Motivation\n\nWhy not use a vanilla Javascript object (or Set or Map) as a hash table?\n\n* A vanilla object has no interface to pre-allocate table capacity in advance,\nand a Set or Map constructor only accepts an iterable. If the Javascript\nengine's underlying implementation of a vanilla object is a hash table, then a\nvanilla object must resize multiple times (copying every key and value multiple\ntimes) while you insert millions of elements.\n\n* A vanilla object has no concept of binary keys. Encoding binary keys as\nhexadecimal or Base64 strings is slow, and Javascript strings have additional\nstorage overhead.\n\n* Extreme GC pause times. Millions of pointers in a vanilla object can block the\nNode.js event loop every few seconds for tens to hundreds of milliseconds at a\ntime, whenever the GC needs to mark every pointer in the object.\n\nA simple comparison, which you can run yourself:\n\n```\nnode vanilla.js\n```\n\nIgnoring any GC implications or per-key memory overhead considerations, which\nare more serious:\n\n```\n\n                       keySize=16 bytes, valueSize=0 bytes\n\n  @ronomon/hash-table: Inserting 4000000 elements...\n  @ronomon/hash-table: 783ms\n\n            new Set(): Inserting 4000000 elements...\n            new Set(): 3695ms\n\n       vanilla object: Inserting 4000000 elements...\n       vanilla object: 5557ms\n\n```\n\n## Fast\n\n`@ronomon/hash-table` features several design decisions and optimizations:\n\n* Each element, a key and corresponding value, can reside in at most 1 of 2\npossible buckets, guaranteeing **constant lookup time** in the worst-case.\n\n* Each bucket contains up to 8 elements to support **a hash table load factor of\n80% or higher**, unlike linear or chained hash tables which support a load\nfactor (`elements / capacity`) of at most 50% and which waste the other 50% of\nmemory. A more efficient load factor means that **table resizes are less\nfrequent**.\n\n* Each element in a bucket has a tag, which is an 8-bit hash of the key. When\nsearching for an element in a bucket, these **8-bit tags are compared first,\neliminating comparisons against most keys in the bucket**.\n\n* **Cache misses across buckets are reduced by an order of magnitude**. In the\ncase of a naive cuckoo hash table, if an element is not in its first bucket then\nits second bucket must be fetched from memory, causing a cache miss, which is\nslow and why cuckoo hashing is sometimes overlooked in favor of\n[linear probing](https://en.wikipedia.org/wiki/Linear_probing) or\n[double hashing](https://en.wikipedia.org/wiki/Double_hashing). However, in our\ndesign, the first bucket has an 8-byte coarse-grained bloom filter (k=1). If the\nelement is not in this bloom filter, the element is guaranteed not to be in\neither of its first or second buckets, and a cache miss can be avoided nearly\n90% of the time.\n\n* The 8-bit tag further contributes to **reduced cache misses within buckets**\nwhen keys or values are large.\n\n* Each element is included in its first bucket's bloom filter, regardless of\nwhether the element is in its first or second bucket, so that **a negative\nlookup can be performed in a single branch** by testing against a single bit in\nthe bloom filter instead of testing against all 8 tags.\n\n* Each bucket has a 1-byte bitmap indicating which of the 8 slots in the bucket\ncontain elements, so that **the first empty slot in a bucket can be found with a\nsingle branch on a 256-byte precomputed lookup table** instead of iterating\nacross all 8 slots.\n\n* Each element's key is hashed with 2 independent hash functions to locate its\nfirst and second buckets. These hashes are computed by interleaving both hash\nfunction lookup tables into **a single interleaved hash function lookup table\nfor optimal locality of reference**.\n\n* The hash function requires keys to be a multiple of 4 bytes and uses an\nunrolled loop, so that **keys are hashed 4 bytes at a time**.\n\n* The number of buckets in a table is a power of 2 for a **fast bitwise mod**.\nWhile Daniel Lemire's excellent [fast alternative to the modulo reduction](https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/)\nyields better entropy mixing of all available bits, it is not used since\nJavascript's 64-bit integer operations are not fast.\n\n* Each bucket is padded to a multiple of 64 bytes for **optimal cache line\nalignment**.\n\n* **An unrolled copy method is selected at instantiation to copy the key and\nvalue without branching** if the key or value is 4, 8, 16, 32, 64, 128, 256\nbytes etc. **A native copy method is used for larger values**.\n\n* Methods accept **a buffer and a buffer offset to avoid a buffer slice**. While\na buffer slice may not copy the underlying memory, it adds overhead through\nallocating an object which must eventually be reclaimed by the GC. This overhead\nis considerable when inserting millions of elements.\n\n* The [CLOCK LRU eviction algorithm](https://en.wikipedia.org/wiki/Page_replacement_algorithm#Clock)\nis supported at an overhead of 2 bits per element, so that **the hash table can\nbe used as a fast user-space cache**.\n\n* Surprisingly, the implementation is **written in Javascript** to avoid the\n[100-200ns bridging cost](https://github.com/nodejs/abi-stable-node/issues/327)\nof calling into C from Javascript. Even a C implementation making use of SIMD\ninstructions may struggle to regain the cost of being called from Javascript.\n\n## Reliable\n\n`@ronomon/hash-table` was designed for billions of elements:\n\n* Each hash table instance is partitioned across multiple buffers and\n**scales linearly up to a maximum of 4,294,967,296 elements or 16 TB of\nmemory**, whichever comes first.\n\n* Exactly **2.5 bytes of overhead per element** at 100% load.\n\n* The implementation is purely in terms of huge flat buffer instances. Apart\nfrom jumping to a huge flat buffer instance, there is **no pointer chasing**.\nMost importantly, this **sidesteps memory fragmentation issues** and is **GC\nfriendly**. If the GC cannot look into the buffers, the GC has nothing further\nto do. This places almost zero load on the GC.\n\n* [Tabulation hashing](https://en.wikipedia.org/wiki/Tabulation_hashing) is used\nas a **fast high-quality hash function** instead of more popular hash functions\nsuch as MurmurHash etc. Tabulation hashing has excellent proven [theoretical\nproperties](http://www2.imm.dtu.dk/projects/thrash-workshop/slides/thorup.pdf),\nguarantees\n[3-independence](https://en.wikipedia.org/wiki/K-independent_hashing), consists\nsolely of table lookups and XOR, is one of the fastest hash functions when\nevaluating hash functions implemented in Javascript, and is **resistant to hash\nflooding attacks**. When compared with hash functions which use magic numbers\nand attempt avalanche through empirical methods, tabulation hashing is easier to\nunderstand and implement, and harder to get wrong.\n\n* Each 8-byte bloom filter has a separate 1-byte count of the number of elements\nin second position, so that **bloom filters can be reset without a runaway\nincrease in false positives**.\n\n* Each 8-byte bloom filter is partitioned into 8 subsidiary filters, so that\n**bloom filters can be reset with minimal latency**. Further, this solves the\nedge case where a key in second position is never removed while other keys are\nchurned repeatedly. Without bloom filter partitioning, the bucket's false\npositive rate would approach 100%. With bloom filter partitioning, only 1/8th of\nthe bucket's filter would be adversely affected.\n\n* **A maximum of 16 cuckoo displacements per insert are allowed in order to\nlimit recursion** and to keep the algorithm intuitive. If both buckets are full\nand no element can be displaced into another bucket, the buffer is resized by a\nfactor of 2 to make space for the insert.\n\n* Each buffer is resized independently of other buffers so that the **resize\nlatency for a massive hash table is bounded per insert**. This guarantee does\nNOT extend to a batch of inserts. For example, subsequent inserts may cause\nother buffers to resize in close succession. But then again, you can **reserve\nor pre-allocate a massive hash table upfront to eliminate resize latency**.\n\n* The number of resize attempts per insert is bounded as a precaution against\nresource exhaustion in the unlikely event that several resizes do not produce an\nempty slot.\n\n## Usage\n\n**var hashTable = new HashTable(keySize, valueSize, [elementsMin],\n[elementsMax])**\n\n* `keySize` An integer, must be a multiple of 4 bytes, up to a maximum of 64\nbytes (`HashTable.KEY_MAX`).\n* `valueSize` An integer, from 0 bytes up to a maximum of 1 MB\n(`HashTable.VALUE_MAX`).\n* `elementsMin` An integer, a *hint* as to the minimum number of elements\nexpected to be inserted, to avoid unnecessary resizing over the short term.\n* `elementsMax` An integer, a *hint* as to the maximum number of elements\nexpected to be inserted, to ensure sufficient capacity over the long term.\n\n**hashTable.set(key, keyOffset, value, valueOffset)**\n\nInserts or updates an element in the hash table.\n\n* `key` A buffer, contains the key to be inserted or updated.\n* `keyOffset` An integer, the offset into `key` at which the key begins.\n* `value` A buffer, contains the value to be inserted or updated.\n* `valueOffset` An integer, the offset into `value` at which the value begins.\n* Returns an integer, `0` if the element was inserted, `1` if the element was\nupdated.\n\n**hashTable.get(key, keyOffset, value, valueOffset)**\n\nRetrieves an element's value from the hash table.\n\n* `key` A buffer, contains the key to be retrieved.\n* `keyOffset` An integer, the offset into `key` at which the key begins.\n* `value` A buffer, the element's value will be copied into this buffer if the\nelement exists.\n* `valueOffset` An integer, the offset into `value` at which to begin copying.\n* Returns an integer, `0` if the element was not found, `1` if the element was\nfound.\n\n**hashTable.exist(key, keyOffset)**\n\nTests whether an element exists in the hash table.\n\n* `key` A buffer, contains the key to be tested.\n* `keyOffset` An integer, the offset into `key` at which the key begins.\n* Returns an integer, `0` if the element was not found, `1` if the element was\nfound.\n\n**hashTable.unset(key, keyOffset)**\n\nRemoves an element from the hash table.\n\n* `key` A buffer, contains the key to be removed.\n* `keyOffset` An integer, the offset into `key` at which the key begins.\n* Returns an integer, `0` if the element was not found, `1` if the element was\nremoved.\n\n**hashTable.cache(key, keyOffset, value, valueOffset)**\n\nSimilar to `set()` but inserts by evicting a least recently used element, rather\nthan resizing the hash table.\n\n* `key` A buffer, contains the key to be inserted or updated.\n* `keyOffset` An integer, the offset into `key` at which the key begins.\n* `value` A buffer, contains the value to be inserted or updated.\n* `valueOffset` An integer, the offset into `value` at which the value begins.\n* Returns an integer, `0` if the element was inserted, `1` if the element was\nupdated, `2` if the element was inserted by evicting another element.\n\n*`cache()` will never resize the hash table. Use the same `elementsMin` and\n`elementsMax` arguments to size the hash table appropriately.*\n\n*`cache()` and `set()` are mutually exclusive and cannot be used on the same\nhash table instance. This restriction is in place to prevent the user from\naccidentally evicting elements which were inserted by `set()`, and to enable\nseveral caching optimizations. When using `cache()`, you can still use `get()`,\n`exist()` and `unset()` to retrieve, test and remove cached elements.*\n\n**hashTable.capacity**\n\nAn integer, read-only, the current total capacity of the hash table, i.e. the\nnumber of elements which the hash table can accommodate at 100% load, which will\nincrease through automatic resizing of the hash table buffers.\n\n**hashTable.length**\n\nAn integer, read-only, the number of elements actually present in the hash\ntable.\n\n**hashTable.load**\n\nA fraction between 0 and 1, read-only, the length of the hash table divided by\nthe capacity of the hash table.\n\n**hashTable.size**\n\nAn integer, read-only, the total size of all hash table buffers in bytes.\n\n### Example\n\n```javascript\nvar HashTable = require('@ronomon/hash-table');\n\nvar keySize = 16;\nvar valueSize = 4;\nvar elementsMin = 1024; // Optional. Reserve space for at least 1,024 elements.\nvar elementsMax = 65536; // Optional. Expect at most 65,536 elements.\n\nvar hashTable = new HashTable(keySize, valueSize, elementsMin, elementsMax);\n\n// set():\nvar key = Buffer.alloc(keySize);\nvar keyOffset = 0;\nvar value = Buffer.alloc(valueSize);\nvar valueOffset = 0;\nvar result = hashTable.set(key, keyOffset, value, valueOffset);\nif (result === 0) console.log('set(): element was inserted');\nif (result === 1) console.log('set(): element was updated');\n\n// get():\nvar result = hashTable.get(key, keyOffset, value, valueOffset);\nif (result === 0) console.log('get(): element does not exist, nothing copied');\nif (result === 1) console.log('get(): element exists, copied value to buffer');\n\n// exist():\nvar result = hashTable.exist(key, keyOffset);\nif (result === 0) console.log('exist(): element does not exist');\nif (result === 1) console.log('exist(): element exists');\n\n// unset():\nvar result = hashTable.unset(key, keyOffset);\nif (result === 0) console.log('unset(): element does not exist, not removed');\nif (result === 1) console.log('unset(): element was removed');\n\n// cache():\n// cache() cannot be used on the same instance as set(), reinstantiate:\nvar hashTable = new HashTable(keySize, valueSize, elementsMin, elementsMax);\nvar result = hashTable.cache(key, keyOffset, value, valueOffset);\nif (result === 0) console.log('cache(): element was inserted');\nif (result === 1) console.log('cache(): element was updated');\nif (result === 2) console.log('cache(): element evicted another element');\n```\n\n### Exceptions\n\nApart from validation exceptions thrown for programming errors, the following\nexceptions may be thrown for operating errors:\n\n**HashTable.ERROR_MAXIMUM_CAPACITY_EXCEEDED**\n\nA hash table buffer could not be further resized due to reaching\n`HashTable.BUFFER_MAX` or `HashTable.BUCKETS_MAX`. Increase `elementsMax` when\ninstantiating the hash table to ensure sufficient capacity.\n\n**HashTable.ERROR_SET**\n\nAn insert failed despite several resize attempts. This should never happen and\nmay indicate weak system entropy.\n\n## Performance\n\n```\n\n            CPU=Intel(R) Xeon(R) CPU E3-1230 V2 @ 3.30GHz\n\n===============================================================================\n            KEY=8 VALUE=0              |            KEY=8 VALUE=4              \n---------------------------------------|---------------------------------------\n      set() Insert              228ns  |      set() Insert              269ns  \n      set() Reserve             134ns  |      set() Reserve             139ns  \n      set() Update              149ns  |      set() Update              164ns  \n      get() Miss                 99ns  |      get() Miss                100ns  \n      get() Hit                 147ns  |      get() Hit                 164ns  \n    exist() Miss                 98ns  |    exist() Miss                 99ns  \n    exist() Hit                 143ns  |    exist() Hit                 148ns  \n    unset() Miss                 98ns  |    unset() Miss                 98ns  \n    unset() Hit                 196ns  |    unset() Hit                 200ns  \n    cache() Insert              122ns  |    cache() Insert              139ns  \n    cache() Evict               153ns  |    cache() Evict               174ns  \n    cache() Miss                120ns  |    cache() Miss                133ns  \n    cache() Hit                 133ns  |    cache() Hit                 136ns  \n===============================================================================\n            KEY=8 VALUE=8              |            KEY=8 VALUE=16             \n---------------------------------------|---------------------------------------\n      set() Insert              299ns  |      set() Insert              346ns  \n      set() Reserve             158ns  |      set() Reserve             172ns  \n      set() Update              175ns  |      set() Update              195ns  \n      get() Miss                 96ns  |      get() Miss                103ns  \n      get() Hit                 174ns  |      get() Hit                 191ns  \n    exist() Miss                 94ns  |    exist() Miss                102ns  \n    exist() Hit                 148ns  |    exist() Hit                 160ns  \n    unset() Miss                 93ns  |    unset() Miss                101ns  \n    unset() Hit                 204ns  |    unset() Hit                 224ns  \n    cache() Insert              151ns  |    cache() Insert              165ns  \n    cache() Evict               189ns  |    cache() Evict               207ns  \n    cache() Miss                150ns  |    cache() Miss                158ns  \n    cache() Hit                 146ns  |    cache() Hit                 166ns  \n===============================================================================\n            KEY=8 VALUE=32             |            KEY=8 VALUE=64             \n---------------------------------------|---------------------------------------\n      set() Insert              418ns  |      set() Insert              631ns  \n      set() Reserve             190ns  |      set() Reserve             275ns  \n      set() Update              225ns  |      set() Update              280ns  \n      get() Miss                104ns  |      get() Miss                111ns  \n      get() Hit                 223ns  |      get() Hit                 277ns  \n    exist() Miss                103ns  |    exist() Miss                109ns  \n    exist() Hit                 172ns  |    exist() Hit                 183ns  \n    unset() Miss                103ns  |    unset() Miss                110ns  \n    unset() Hit                 251ns  |    unset() Hit                 295ns  \n    cache() Insert              192ns  |    cache() Insert              244ns  \n    cache() Evict               229ns  |    cache() Evict               286ns  \n    cache() Miss                167ns  |    cache() Miss                179ns  \n    cache() Hit                 181ns  |    cache() Hit                 191ns  \n===============================================================================\n            KEY=8 VALUE=4096           |            KEY=8 VALUE=65536          \n---------------------------------------|---------------------------------------\n      set() Insert             6436ns  |      set() Insert            79601ns  \n      set() Reserve            2178ns  |      set() Reserve           24260ns  \n      set() Update             1518ns  |      set() Update            20550ns  \n      get() Miss                135ns  |      get() Miss                181ns  \n      get() Hit                 828ns  |      get() Hit                7904ns  \n    exist() Miss                127ns  |    exist() Miss                171ns  \n    exist() Hit                 232ns  |    exist() Hit                 269ns  \n    unset() Miss                104ns  |    unset() Miss                 63ns  \n    unset() Hit                1088ns  |    unset() Hit               13284ns  \n    cache() Insert             1746ns  |    cache() Insert            20381ns  \n    cache() Evict              1471ns  |    cache() Evict             20526ns  \n    cache() Miss                180ns  |    cache() Miss                224ns  \n    cache() Hit                 231ns  |    cache() Hit                 283ns  \n===============================================================================\n            KEY=16 VALUE=0             |            KEY=16 VALUE=4             \n---------------------------------------|---------------------------------------\n      set() Insert              339ns  |      set() Insert              389ns  \n      set() Reserve             161ns  |      set() Reserve             182ns  \n      set() Update              197ns  |      set() Update              211ns  \n      get() Miss                114ns  |      get() Miss                115ns  \n      get() Hit                 194ns  |      get() Hit                 208ns  \n    exist() Miss                113ns  |    exist() Miss                112ns  \n    exist() Hit                 197ns  |    exist() Hit                 189ns  \n    unset() Miss                112ns  |    unset() Miss                111ns  \n    unset() Hit                 250ns  |    unset() Hit                 423ns  \n    cache() Insert              164ns  |    cache() Insert              184ns  \n    cache() Evict               206ns  |    cache() Evict               225ns  \n    cache() Miss                163ns  |    cache() Miss                166ns  \n    cache() Hit                 183ns  |    cache() Hit                 189ns  \n===============================================================================\n            KEY=16 VALUE=8             |            KEY=16 VALUE=16            \n---------------------------------------|---------------------------------------\n      set() Insert              416ns  |      set() Insert              444ns  \n      set() Reserve             189ns  |      set() Reserve             192ns  \n      set() Update              225ns  |      set() Update              240ns  \n      get() Miss                124ns  |      get() Miss                120ns  \n      get() Hit                 221ns  |      get() Hit                 238ns  \n    exist() Miss                122ns  |    exist() Miss                119ns  \n    exist() Hit                 199ns  |    exist() Hit                 205ns  \n    unset() Miss                122ns  |    unset() Miss                119ns  \n    unset() Hit                 458ns  |    unset() Hit                 509ns  \n    cache() Insert              185ns  |    cache() Insert              197ns  \n    cache() Evict               232ns  |    cache() Evict               243ns  \n    cache() Miss                176ns  |    cache() Miss                179ns  \n    cache() Hit                 195ns  |    cache() Hit                 202ns  \n===============================================================================\n            KEY=16 VALUE=32            |            KEY=16 VALUE=64            \n---------------------------------------|---------------------------------------\n      set() Insert              527ns  |      set() Insert              521ns  \n      set() Reserve             242ns  |      set() Reserve             324ns  \n      set() Update              268ns  |      set() Update              331ns  \n      get() Miss                125ns  |      get() Miss                122ns  \n      get() Hit                 265ns  |      get() Hit                 328ns  \n    exist() Miss                125ns  |    exist() Miss                120ns  \n    exist() Hit                 217ns  |    exist() Hit                 234ns  \n    unset() Miss                128ns  |    unset() Miss                121ns  \n    unset() Hit                 619ns  |    unset() Hit                 484ns  \n    cache() Insert              226ns  |    cache() Insert              279ns  \n    cache() Evict               263ns  |    cache() Evict               316ns  \n    cache() Miss                185ns  |    cache() Miss                206ns  \n    cache() Hit                 212ns  |    cache() Hit                 224ns  \n===============================================================================\n            KEY=16 VALUE=4096          |            KEY=16 VALUE=65536         \n---------------------------------------|---------------------------------------\n      set() Insert             6264ns  |      set() Insert            80181ns  \n      set() Reserve            2172ns  |      set() Reserve           23874ns  \n      set() Update             1535ns  |      set() Update            20602ns  \n      get() Miss                155ns  |      get() Miss                204ns  \n      get() Hit                 863ns  |      get() Hit                7758ns  \n    exist() Miss                148ns  |    exist() Miss                186ns  \n    exist() Hit                 272ns  |    exist() Hit                 343ns  \n    unset() Miss                130ns  |    unset() Miss                150ns  \n    unset() Hit                1228ns  |    unset() Hit               13332ns  \n    cache() Insert             1767ns  |    cache() Insert            20791ns  \n    cache() Evict              1486ns  |    cache() Evict             20552ns  \n    cache() Miss                201ns  |    cache() Miss                243ns  \n    cache() Hit                 266ns  |    cache() Hit                 318ns  \n===============================================================================\n            KEY=32 VALUE=0             |            KEY=32 VALUE=4             \n---------------------------------------|---------------------------------------\n      set() Insert              526ns  |      set() Insert              549ns  \n      set() Reserve             239ns  |      set() Reserve             251ns  \n      set() Update              295ns  |      set() Update              302ns  \n      get() Miss                160ns  |      get() Miss                161ns  \n      get() Hit                 293ns  |      get() Hit                 302ns  \n    exist() Miss                158ns  |    exist() Miss                158ns  \n    exist() Hit                 281ns  |    exist() Hit                 282ns  \n    unset() Miss                159ns  |    unset() Miss                158ns  \n    unset() Hit                 590ns  |    unset() Hit                 616ns  \n    cache() Insert              239ns  |    cache() Insert              249ns  \n    cache() Evict               272ns  |    cache() Evict               282ns  \n    cache() Miss                217ns  |    cache() Miss                224ns  \n    cache() Hit                 271ns  |    cache() Hit                 277ns  \n===============================================================================\n            KEY=32 VALUE=8             |            KEY=32 VALUE=16            \n---------------------------------------|---------------------------------------\n      set() Insert              562ns  |      set() Insert              625ns  \n      set() Reserve             244ns  |      set() Reserve             277ns  \n      set() Update              316ns  |      set() Update              325ns  \n      get() Miss                164ns  |      get() Miss                162ns  \n      get() Hit                 314ns  |      get() Hit                 325ns  \n    exist() Miss                161ns  |    exist() Miss                159ns  \n    exist() Hit                 290ns  |    exist() Hit                 291ns  \n    unset() Miss                162ns  |    unset() Miss                159ns  \n    unset() Hit                 646ns  |    unset() Hit                 693ns  \n    cache() Insert              243ns  |    cache() Insert              261ns  \n    cache() Evict               290ns  |    cache() Evict               301ns  \n    cache() Miss                227ns  |    cache() Miss                226ns  \n    cache() Hit                 277ns  |    cache() Hit                 283ns  \n===============================================================================\n            KEY=32 VALUE=32            |            KEY=32 VALUE=64            \n---------------------------------------|---------------------------------------\n      set() Insert              685ns  |      set() Insert              739ns  \n      set() Reserve             308ns  |      set() Reserve             356ns  \n      set() Update              348ns  |      set() Update              399ns  \n      get() Miss                161ns  |      get() Miss                154ns  \n      get() Hit                 347ns  |      get() Hit                 398ns  \n    exist() Miss                163ns  |    exist() Miss                154ns  \n    exist() Hit                 296ns  |    exist() Hit                 304ns  \n    unset() Miss                162ns  |    unset() Miss                155ns  \n    unset() Hit                 799ns  |    unset() Hit                 662ns  \n    cache() Insert              276ns  |    cache() Insert              360ns  \n    cache() Evict               322ns  |    cache() Evict               376ns  \n    cache() Miss                231ns  |    cache() Miss                272ns  \n    cache() Hit                 289ns  |    cache() Hit                 299ns  \n===============================================================================\n            KEY=32 VALUE=4096          |            KEY=32 VALUE=65536         \n---------------------------------------|---------------------------------------\n      set() Insert             6761ns  |      set() Insert            78095ns  \n      set() Reserve            2309ns  |      set() Reserve           23743ns  \n      set() Update             1611ns  |      set() Update            20693ns  \n      get() Miss                202ns  |      get() Miss                250ns  \n      get() Hit                 952ns  |      get() Hit                7802ns  \n    exist() Miss                190ns  |    exist() Miss                227ns  \n    exist() Hit                 354ns  |    exist() Hit                 444ns  \n    unset() Miss                174ns  |    unset() Miss                199ns  \n    unset() Hit                1383ns  |    unset() Hit               13594ns  \n    cache() Insert             1868ns  |    cache() Insert            20883ns  \n    cache() Evict              1530ns  |    cache() Evict             20565ns  \n    cache() Miss                246ns  |    cache() Miss                295ns  \n    cache() Hit                 339ns  |    cache() Hit                 401ns  \n===============================================================================\n            KEY=64 VALUE=0             |            KEY=64 VALUE=4             \n---------------------------------------|---------------------------------------\n      set() Insert              947ns  |      set() Insert              961ns  \n      set() Reserve             378ns  |      set() Reserve             370ns  \n      set() Update              463ns  |      set() Update              471ns  \n      get() Miss                240ns  |      get() Miss                236ns  \n      get() Hit                 465ns  |      get() Hit                 471ns  \n    exist() Miss                239ns  |    exist() Miss                236ns  \n    exist() Hit                 452ns  |    exist() Hit                 452ns  \n    unset() Miss                240ns  |    unset() Miss                237ns  \n    unset() Hit                 628ns  |    unset() Hit                 650ns  \n    cache() Insert              386ns  |    cache() Insert              383ns  \n    cache() Evict               407ns  |    cache() Evict               412ns  \n    cache() Miss                315ns  |    cache() Miss                323ns  \n    cache() Hit                 432ns  |    cache() Hit                 437ns  \n===============================================================================\n            KEY=64 VALUE=8             |            KEY=64 VALUE=16            \n---------------------------------------|---------------------------------------\n      set() Insert             1008ns  |      set() Insert              837ns  \n      set() Reserve             378ns  |      set() Reserve             439ns  \n      set() Update              478ns  |      set() Update              501ns  \n      get() Miss                235ns  |      get() Miss                231ns  \n      get() Hit                 475ns  |      get() Hit                 498ns  \n    exist() Miss                235ns  |    exist() Miss                230ns  \n    exist() Hit                 451ns  |    exist() Hit                 466ns  \n    unset() Miss                236ns  |    unset() Miss                230ns  \n    unset() Hit                 674ns  |    unset() Hit                 720ns  \n    cache() Insert              376ns  |    cache() Insert              390ns  \n    cache() Evict               421ns  |    cache() Evict               434ns  \n    cache() Miss                338ns  |    cache() Miss                353ns  \n    cache() Hit                 444ns  |    cache() Hit                 448ns  \n===============================================================================\n            KEY=64 VALUE=32            |            KEY=64 VALUE=64            \n---------------------------------------|---------------------------------------\n      set() Insert              929ns  |      set() Insert             1293ns  \n      set() Reserve             438ns  |      set() Reserve             516ns  \n      set() Update              511ns  |      set() Update              555ns  \n      get() Miss                227ns  |      get() Miss                231ns  \n      get() Hit                 507ns  |      get() Hit                 553ns  \n    exist() Miss                227ns  |    exist() Miss                227ns  \n    exist() Hit                 459ns  |    exist() Hit                 457ns  \n    unset() Miss                227ns  |    unset() Miss                229ns  \n    unset() Hit                 821ns  |    unset() Hit                 688ns  \n    cache() Insert              440ns  |    cache() Insert              479ns  \n    cache() Evict               460ns  |    cache() Evict               485ns  \n    cache() Miss                386ns  |    cache() Miss                302ns  \n    cache() Hit                 454ns  |    cache() Hit                 435ns  \n===============================================================================\n            KEY=64 VALUE=4096          |            KEY=64 VALUE=65536         \n---------------------------------------|---------------------------------------\n      set() Insert             7004ns  |      set() Insert            81742ns  \n      set() Reserve            2502ns  |      set() Reserve           24197ns  \n      set() Update             1776ns  |      set() Update            20955ns  \n      get() Miss                268ns  |      get() Miss                326ns  \n      get() Hit                1166ns  |      get() Hit                8035ns  \n    exist() Miss                259ns  |    exist() Miss                297ns  \n    exist() Hit                 515ns  |    exist() Hit                 592ns  \n    unset() Miss                249ns  |    unset() Miss                266ns  \n    unset() Hit                1490ns  |    unset() Hit               13617ns  \n    cache() Insert             2004ns  |    cache() Insert            21043ns  \n    cache() Evict              1671ns  |    cache() Evict             20711ns  \n    cache() Miss                336ns  |    cache() Miss                383ns  \n    cache() Hit                 492ns  |    cache() Hit                 551ns  \n```\n\n## Tests\n\n`@ronomon/hash-table` ships with extensive tests, including a fuzz test:\n\n```\nnode test.js\n```\n\n## Benchmark\n\n```\nnode benchmark.js\n```\n","funding_links":[],"categories":["Algorithms and Data Structures"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronomon%2Fhash-table","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fronomon%2Fhash-table","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronomon%2Fhash-table/lists"}