Skip to content

Commit

Permalink
Allow custom key type
Browse files Browse the repository at this point in the history
  • Loading branch information
jakoschiko committed Oct 31, 2024
1 parent e7edd6e commit 61d42ce
Show file tree
Hide file tree
Showing 17 changed files with 710 additions and 243 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ name = "intmap"
version = "2.0.0"
edition = "2021"
rust-version = "1.58"
description = "Specialized HashMap for u64 keys"
description = "Specialized HashMap for integer keys"
authors = ["Jesper Axelsson <jesperaxe@gmail.com>"]
readme = "README.md"
license = "MIT"
repository = "https://github.com/JesperAxelsson/rust-intmap"
keywords = ["hashmap", "u64", "intmap"]
keywords = ["hashmap", "int", "intmap"]

[dependencies]
serde = { version = "1.0", optional = true, default-features = false }
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[![crates.io](https://img.shields.io/crates/v/intmap.svg)](https://crates.io/crates/intmap)

# rust-intmap
Specialized hashmap for `u64` keys
Specialized hashmap for integer keys.

Might be missing some functionality but you can remove, add, get and clear for now.

Expand Down Expand Up @@ -53,12 +53,12 @@ for i in 0..20_000 {
```

# How can it be so much faster?
I use a specialized hash function for `u64` which multiplies the key with the largest prime for `u64`. By keeping the internal cache a power 2 you can avoid the expensive modulus operator as mentioned in [this Stack Overflow post](http://stackoverflow.com/questions/6670715/mod-of-power-2-on-bitwise-operators). The hash function looks like this:
I use a specialized hash function for integers which multiplies the key with the their largest prime. By keeping the internal cache a power 2 you can avoid the expensive modulus operator as mentioned in [this Stack Overflow post](http://stackoverflow.com/questions/6670715/mod-of-power-2-on-bitwise-operators). The hash function looks like this:

```rust
#[inline]
fn hash_u64(seed: u64) -> u64 {
let a = 11400714819323198549u64;
let a = 18446744073709551611u64;
let val = a.wrapping_mul(seed);
val
}
Expand Down
1 change: 1 addition & 0 deletions integration_tests/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions integration_tests/benchmark/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ rand = "0.8.5"
[[bench]]
name = "basic_bench"
harness = false

[[bench]]
name = "key_comparison"
harness = false
79 changes: 40 additions & 39 deletions integration_tests/benchmark/benches/basic_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ fn main() {
#[bench]
fn u64_insert_built_in(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map = HashMap::with_capacity(data.len());
let mut map: HashMap<u64, u64> = HashMap::with_capacity(data.len());

bencher.bench_local(|| {
map.clear();

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}
});
}
Expand All @@ -33,10 +33,10 @@ fn u64_insert_without_capacity_built_in(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);

bencher.bench_local(|| {
let mut map = HashMap::new();
let mut map: HashMap<u64, u64> = HashMap::new();

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}

black_box(&map);
Expand All @@ -46,10 +46,10 @@ fn u64_insert_without_capacity_built_in(bencher: Bencher) {
#[bench]
fn u64_get_built_in(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map: HashMap<&u64, &u64> = HashMap::with_capacity(data.len());
let mut map: HashMap<u64, u64> = HashMap::with_capacity(data.len());

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}

bencher.bench_local(|| {
Expand Down Expand Up @@ -87,13 +87,14 @@ impl std::hash::BuildHasher for NoOpHasher {
#[bench]
fn u64_insert_no_op(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map = HashMap::with_capacity_and_hasher(data.len(), NoOpHasher(0));
let mut map: HashMap<u64, u64, NoOpHasher> =
HashMap::with_capacity_and_hasher(data.len(), NoOpHasher(0));

bencher.bench_local(|| {
map.clear();

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}
});
}
Expand All @@ -103,10 +104,10 @@ fn u64_insert_without_capacity_no_op(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);

bencher.bench_local(|| {
let mut map = HashMap::with_hasher(NoOpHasher(0));
let mut map: HashMap<u64, u64, NoOpHasher> = HashMap::with_hasher(NoOpHasher(0));

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}

black_box(&map);
Expand All @@ -116,11 +117,11 @@ fn u64_insert_without_capacity_no_op(bencher: Bencher) {
#[bench]
fn u64_get_no_op(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map: HashMap<&u64, &u64, NoOpHasher> =
let mut map: HashMap<u64, u64, NoOpHasher> =
HashMap::with_capacity_and_hasher(data.len(), NoOpHasher(0));

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}

bencher.bench_local(|| {
Expand All @@ -135,13 +136,13 @@ fn u64_get_no_op(bencher: Bencher) {
#[bench]
fn u64_insert_brown(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map = BrownMap::with_capacity(data.len());
let mut map: BrownMap<u64, u64> = BrownMap::with_capacity(data.len());

bencher.bench_local(|| {
map.clear();

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}
});
}
Expand All @@ -151,10 +152,10 @@ fn u64_insert_without_capacity_brown(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);

bencher.bench_local(|| {
let mut map = BrownMap::new();
let mut map: BrownMap<u64, u64> = BrownMap::new();

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}

black_box(&map);
Expand All @@ -164,10 +165,10 @@ fn u64_insert_without_capacity_brown(bencher: Bencher) {
#[bench]
fn u64_get_brown(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map: BrownMap<&u64, &u64> = BrownMap::with_capacity(data.len());
let mut map: BrownMap<u64, u64> = BrownMap::with_capacity(data.len());

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}

bencher.bench_local(|| {
Expand All @@ -182,13 +183,13 @@ fn u64_get_brown(bencher: Bencher) {
#[bench]
fn u64_insert_ahash(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map = AHashMap::with_capacity(data.len());
let mut map: AHashMap<u64, u64> = AHashMap::with_capacity(data.len());

bencher.bench_local(|| {
map.clear();

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}
});
}
Expand All @@ -198,10 +199,10 @@ fn u64_insert_without_capacity_ahash(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);

bencher.bench_local(|| {
let mut map = AHashMap::new();
let mut map: AHashMap<u64, u64> = AHashMap::new();

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}

black_box(&map);
Expand All @@ -211,10 +212,10 @@ fn u64_insert_without_capacity_ahash(bencher: Bencher) {
#[bench]
fn u64_get_ahash(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map: AHashMap<&u64, &u64> = AHashMap::with_capacity(data.len());
let mut map: AHashMap<u64, u64> = AHashMap::with_capacity(data.len());

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}

bencher.bench_local(|| {
Expand All @@ -229,24 +230,24 @@ fn u64_get_ahash(bencher: Bencher) {
#[bench]
fn u64_insert_indexmap(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map = IndexMap::with_capacity(data.len());
let mut map: IndexMap<u64, u64> = IndexMap::with_capacity(data.len());

bencher.bench_local(|| {
map.clear();

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}
});
}

#[bench]
fn u64_get_indexmap(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map: IndexMap<&u64, &u64> = IndexMap::with_capacity(data.len());
let mut map: IndexMap<u64, u64> = IndexMap::with_capacity(data.len());

for s in data.iter() {
black_box(map.insert(s, s));
black_box(map.insert(*s, *s));
}

bencher.bench_local(|| {
Expand All @@ -261,27 +262,27 @@ fn u64_get_indexmap(bencher: Bencher) {
#[bench]
fn u64_insert_intmap(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map = IntMap::with_capacity(data.len());
let mut map: IntMap<u64, u64> = IntMap::with_capacity(data.len());

bencher.bench_local(|| {
map.clear();

for s in data.iter() {
black_box(map.insert(*s, s));
black_box(map.insert(*s, *s));
}
});
}

#[bench]
fn u64_insert_intmap_checked(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);
let mut map = IntMap::with_capacity(data.len());
let mut map: IntMap<u64, u64> = IntMap::with_capacity(data.len());

bencher.bench_local(|| {
map.clear();

for s in data.iter() {
black_box(map.insert_checked(*s, s));
black_box(map.insert_checked(*s, *s));
}
});
}
Expand All @@ -290,15 +291,15 @@ fn u64_insert_intmap_checked(bencher: Bencher) {
fn u64_insert_intmap_entry(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);

let mut map = IntMap::with_capacity(data.len());
let mut map: IntMap<u64, u64> = IntMap::with_capacity(data.len());

bencher.bench_local(|| {
map.clear();

for s in data.iter() {
black_box(match map.entry(*s) {
Entry::Occupied(_) => panic!("unexpected while insert, i = {}", s),
Entry::Vacant(entry) => entry.insert(s),
Entry::Vacant(entry) => entry.insert(*s),
});
}
});
Expand All @@ -309,10 +310,10 @@ fn u64_insert_without_capacity_intmap(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);

bencher.bench_local(|| {
let mut map = IntMap::new();
let mut map: IntMap<u64, u64> = IntMap::new();

for s in data.iter() {
black_box(map.insert(*s, s));
black_box(map.insert(*s, *s));
}

black_box(&map);
Expand All @@ -322,7 +323,7 @@ fn u64_insert_without_capacity_intmap(bencher: Bencher) {
#[bench]
fn u64_resize_intmap(bencher: Bencher) {
bencher.bench_local(|| {
let mut map: IntMap<u64> = IntMap::new();
let mut map: IntMap<u64, u64> = IntMap::new();
map.reserve(VEC_COUNT);
black_box(&map);
});
Expand All @@ -332,9 +333,9 @@ fn u64_resize_intmap(bencher: Bencher) {
fn u64_get_intmap(bencher: Bencher) {
let data = get_random_range(VEC_COUNT);

let mut map = IntMap::with_capacity(data.len());
let mut map: IntMap<u64, u64> = IntMap::with_capacity(data.len());
for s in data.iter() {
map.insert(*s, s);
map.insert(*s, *s);
}

bencher.bench_local(|| {
Expand Down
Loading

0 comments on commit 61d42ce

Please sign in to comment.