Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable specialization with aHash #207

Merged
merged 16 commits into from
Dec 10, 2020
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ edition = "2018"

[dependencies]
# For the default hasher
ahash = { version = "0.4.4", optional = true, default-features = false }
ahash = { version = "0.5.1", default-features = false, optional = true }

# For external trait impls
rayon = { version = "1.0", optional = true }
Expand All @@ -25,7 +25,7 @@ compiler_builtins = { version = "0.1.2", optional = true }
alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-alloc" }

[dev-dependencies]
lazy_static = "1.2"
lazy_static = "1.4"
rand = { version = "0.7.3", features = ["small_rng"] }
rayon = "1.0"
rustc-hash = "=1.0"
Expand All @@ -34,9 +34,9 @@ doc-comment = "0.3.1"

[features]
default = ["ahash", "inline-more"]

std = ["ahash/std", "serde/std"]
ahash-compile-time-rng = ["ahash/compile-time-rng"]
nightly = []
nightly = ["ahash/specialize"]
rustc-internal-api = []
rustc-dep-of-std = [
"nightly",
Expand Down
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ in environments without `std`, such as embedded systems and kernels.
## Features

- Drop-in replacement for the standard library `HashMap` and `HashSet` types.
- Uses `AHash` as the default hasher, which is much faster than SipHash.
- Uses [AHash](https://github.com/tkaitchuck/aHash) as the default hasher, which is much faster than SipHash.
- Around 2x faster than the previous standard library `HashMap`.
- Lower memory usage: only 1 byte of overhead per entry instead of 8.
- Compatible with `#[no_std]` (but requires a global allocator with the `alloc` crate).
Expand All @@ -37,7 +37,7 @@ in environments without `std`, such as embedded systems and kernels.

Compared to the previous implementation of `std::collections::HashMap` (Rust 1.35).

With the hashbrown default AHash hasher (not HashDoS-resistant):
With the hashbrown default AHash hasher ([without HashDoS-resistance](#Flags)):

```text
name oldstdhash ns/iter hashbrown ns/iter diff ns/iter diff % speedup
Expand Down Expand Up @@ -96,19 +96,22 @@ use hashbrown::HashMap;
let mut map = HashMap::new();
map.insert(1, "one");
```

## Flags
This crate has the following Cargo features:

- `inline-more`: Adds inline hints to most functions, improving run-time performance at the cost
of compilation time. (enabled by default)
- `ahash`: Compiles with ahash as default hasher. (enabled by default)
- `std`: Enables use of features that depend on the standard library.
tkaitchuck marked this conversation as resolved.
Show resolved Hide resolved
If `ahash` is used this includes using random keys to provide DOS resistance. (disabled by default)
- `ahash-compile-time-rng`: This is an alternative to `std` which still allows for some degree of DOS-resistance.
However, it can result in issues for certain platforms.
tkaitchuck marked this conversation as resolved.
Show resolved Hide resolved
See details in [issue#124](https://github.com/rust-lang/hashbrown/issues/124). (disabled by default)
- `nightly`: Enables nightly-only features: `#[may_dangle]`.
- `serde`: Enables serde serialization support.
- `rayon`: Enables rayon parallel iterator support.
- `raw`: Enables access to the experimental and unsafe `RawTable` API.
- `inline-more`: Adds inline hints to most functions, improving run-time performance at the cost
of compilation time. (enabled by default)
- `ahash`: Compiles with ahash as default hasher. (enabled by default)
- `ahash-compile-time-rng`: Activates the `compile-time-rng` feature of ahash, to increase the
DOS-resistance, but can result in issues for `no_std` builds. More details in
[issue#124](https://github.com/rust-lang/hashbrown/issues/124). (enabled by default)


## License

Expand Down
57 changes: 35 additions & 22 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@ use crate::raw::{Bucket, RawDrain, RawIntoIter, RawIter, RawTable};
use crate::TryReserveError;
use core::borrow::Borrow;
use core::fmt::{self, Debug};
use core::hash::{BuildHasher, Hash, Hasher};
use core::hash::{BuildHasher, Hash};
use core::iter::{FromIterator, FusedIterator};
use core::marker::PhantomData;
use core::mem;
use core::ops::Index;

/// Default hasher for `HashMap`.
#[cfg(feature = "ahash")]
#[cfg(all(
feature = "ahash",
any(feature = "std", feature = "ahash-compile-time-rng")
))]
pub type DefaultHashBuilder = ahash::RandomState;

/// Default hasher for `HashMap`.
#[cfg(all(
feature = "ahash",
not(any(feature = "std", feature = "ahash-compile-time-rng"))
))]
pub type DefaultHashBuilder = core::hash::BuildHasherDefault<ahash::AHasher>;

/// Dummy default hasher for `HashMap`.
#[cfg(not(feature = "ahash"))]
pub enum DefaultHashBuilder {}
Expand Down Expand Up @@ -239,9 +249,19 @@ where

#[cfg_attr(feature = "inline-more", inline)]
pub(crate) fn make_hash<K: Hash + ?Sized>(hash_builder: &impl BuildHasher, val: &K) -> u64 {
let mut state = hash_builder.build_hasher();
val.hash(&mut state);
state.finish()
#[cfg(feature = "ahash")]
tkaitchuck marked this conversation as resolved.
Show resolved Hide resolved
{
use ahash::CallHasher;
let state = hash_builder.build_hasher();
val.get_hash(state)
}
#[cfg(not(feature = "ahash"))]
{
use core::hash::Hasher;
let mut state = hash_builder.build_hasher();
val.hash(&mut state);
state.finish()
}
}

#[cfg(feature = "ahash")]
Expand Down Expand Up @@ -1541,9 +1561,8 @@ impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> {
K: Borrow<Q>,
Q: Hash + Eq,
{
let mut hasher = self.map.hash_builder.build_hasher();
k.hash(&mut hasher);
self.from_key_hashed_nocheck(hasher.finish(), k)
let hash = make_hash(&self.map.hash_builder, k);
self.from_key_hashed_nocheck(hash, k)
}

/// Creates a `RawEntryMut` from the given key and its hash.
Expand Down Expand Up @@ -1598,9 +1617,8 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> {
K: Borrow<Q>,
Q: Hash + Eq,
{
let mut hasher = self.map.hash_builder.build_hasher();
k.hash(&mut hasher);
self.from_key_hashed_nocheck(hasher.finish(), k)
let hash = make_hash(&self.map.hash_builder, k);
self.from_key_hashed_nocheck(hash, k)
}

/// Access an entry by a key and its hash.
Expand Down Expand Up @@ -1957,9 +1975,8 @@ impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> {
K: Hash,
S: BuildHasher,
{
let mut hasher = self.hash_builder.build_hasher();
key.hash(&mut hasher);
self.insert_hashed_nocheck(hasher.finish(), key, value)
let hash = make_hash(self.hash_builder, &key);
self.insert_hashed_nocheck(hash, key, value)
}

/// Sets the value of the entry with the VacantEntry's key,
Expand Down Expand Up @@ -2001,14 +2018,10 @@ impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> {
K: Hash,
S: BuildHasher,
{
let mut hasher = self.hash_builder.build_hasher();
key.hash(&mut hasher);

let elem = self.table.insert(
hasher.finish(),
(key, value),
make_hasher(self.hash_builder),
);
let hash = make_hash(self.hash_builder, &key);
let elem = self
.table
.insert(hash, (key, value), make_hasher(self.hash_builder));
RawOccupiedEntryMut {
elem,
table: self.table,
Expand Down