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

Make FxHash the default HashMap hasher #7107

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions crates/bevy_asset/src/path.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_utils::AHasher;
use bevy_utils::FxHasher;
use serde::{Deserialize, Serialize};
use std::{
borrow::Cow,
Expand Down Expand Up @@ -87,7 +87,7 @@ pub struct LabelId(u64);

impl<'a> From<&'a Path> for SourcePathId {
fn from(value: &'a Path) -> Self {
let mut hasher = get_hasher();
let mut hasher = FxHasher::default();
value.hash(&mut hasher);
SourcePathId(hasher.finish())
}
Expand All @@ -107,7 +107,7 @@ impl<'a> From<AssetPath<'a>> for SourcePathId {

impl<'a> From<Option<&'a str>> for LabelId {
fn from(value: Option<&'a str>) -> Self {
let mut hasher = get_hasher();
let mut hasher = FxHasher::default();
value.hash(&mut hasher);
LabelId(hasher.finish())
}
Expand All @@ -125,11 +125,6 @@ impl AssetPathId {
}
}

/// this hasher provides consistent results across runs
pub(crate) fn get_hasher() -> AHasher {
AHasher::new_with_keys(42, 23)
}

impl<'a, T> From<T> for AssetPathId
where
T: Into<AssetPath<'a>>,
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_core/src/name.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::Reflect;
use bevy_reflect::{std_traits::ReflectDefault, FromReflect};
use bevy_utils::AHasher;
use bevy_utils::FxHasher;
use std::{
borrow::Cow,
hash::{Hash, Hasher},
Expand Down Expand Up @@ -63,7 +63,7 @@ impl Name {
}

fn update_hash(&mut self) {
let mut hasher = AHasher::default();
let mut hasher = FxHasher::default();
self.name.hash(&mut hasher);
self.hash = hasher.finish();
}
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_diagnostic/src/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bevy_ecs::system::Resource;
use bevy_log::warn;
use bevy_utils::{Duration, Instant, StableHashMap, Uuid};
use bevy_utils::{Duration, HashMap, Instant, Uuid};
use std::{borrow::Cow, collections::VecDeque};

use crate::MAX_DIAGNOSTIC_NAME_WIDTH;
Expand Down Expand Up @@ -200,9 +200,9 @@ impl Diagnostic {
/// A collection of [Diagnostic]s
#[derive(Debug, Default, Resource)]
pub struct Diagnostics {
// This uses a [`StableHashMap`] to ensure that the iteration order is deterministic between
// This uses a [`HashMap`] to ensure that the iteration order is deterministic between
// runs when all diagnostics are inserted in the same order.
diagnostics: StableHashMap<DiagnosticId, Diagnostic>,
diagnostics: HashMap<DiagnosticId, Diagnostic>,
}

impl Diagnostics {
Expand Down
1 change: 0 additions & 1 deletion crates/bevy_ecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ async-channel = "1.4"
event-listener = "2.5"
thread_local = "1.1.4"
fixedbitset = "0.4"
fxhash = "0.2"
downcast-rs = "1.2"
serde = { version = "1", features = ["derive"] }

Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
};
pub use bevy_ecs_macros::Component;
use bevy_ptr::{OwningPtr, UnsafeCellDeref};
use bevy_utils::HashMap;
use std::cell::UnsafeCell;
use std::{
alloc::Layout,
Expand Down Expand Up @@ -398,8 +399,8 @@ impl ComponentDescriptor {
#[derive(Debug, Default)]
pub struct Components {
components: Vec<ComponentInfo>,
indices: std::collections::HashMap<TypeId, usize, fxhash::FxBuildHasher>,
resource_indices: std::collections::HashMap<TypeId, usize, fxhash::FxBuildHasher>,
indices: HashMap<TypeId, usize>,
resource_indices: HashMap<TypeId, usize>,
}

impl Components {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/query/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
/// Returns a vector of elements that this set and `other` cannot access at the same time.
pub fn get_conflicts(&self, other: &FilteredAccessSet<T>) -> Vec<T> {
// if the unfiltered access is incompatible, must check each pair
let mut conflicts = HashSet::new();
let mut conflicts = HashSet::default();
if !self.combined_access.is_compatible(other.combined_access()) {
for filtered in &self.filtered_accesses {
for other_filtered in &other.filtered_accesses {
Expand All @@ -388,7 +388,7 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
/// Returns a vector of elements that this set and `other` cannot access at the same time.
pub fn get_conflicts_single(&self, filtered_access: &FilteredAccess<T>) -> Vec<T> {
// if the unfiltered access is incompatible, must check each pair
let mut conflicts = HashSet::new();
let mut conflicts = HashSet::default();
if !self.combined_access.is_compatible(filtered_access.access()) {
for filtered in &self.filtered_accesses {
conflicts.extend(filtered.get_conflicts(filtered_access).into_iter());
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_gltf/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ async fn load_gltf<'a, 'b>(

#[cfg(feature = "bevy_animation")]
let paths = {
let mut paths = HashMap::<usize, (usize, Vec<Name>)>::new();
let mut paths = HashMap::<usize, (usize, Vec<Name>)>::default();
for scene in gltf.scenes() {
for node in scene.nodes() {
let root_index = node.index();
Expand Down Expand Up @@ -461,8 +461,8 @@ async fn load_gltf<'a, 'b>(
for scene in gltf.scenes() {
let mut err = None;
let mut world = World::default();
let mut node_index_to_entity_map = HashMap::new();
let mut entity_to_skin_index_map = HashMap::new();
let mut node_index_to_entity_map = HashMap::default();
let mut entity_to_skin_index_map = HashMap::default();

world
.spawn(SpatialBundle::INHERITED_IDENTITY)
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell};
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
use bevy_utils::{Duration, Instant};
use bevy_utils::{HashMap, HashSet};
use bevy_utils::{FixedState, HashMap, HashSet};
use std::{
any::Any,
borrow::Cow,
Expand Down Expand Up @@ -509,7 +509,7 @@ where
impl<K: FromReflect + Eq + Hash, V: FromReflect> FromReflect for HashMap<K, V> {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Map(ref_map) = reflect.reflect_ref() {
let mut new_map = Self::with_capacity(ref_map.len());
let mut new_map = Self::with_capacity_and_hasher(ref_map.len(), FixedState);
for (key, value) in ref_map.iter() {
let new_key = K::from_reflect(key)?;
let new_value = V::from_reflect(value)?;
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{
};

use crate::utility::NonGenericTypeInfoCell;
pub use bevy_utils::AHasher as ReflectHasher;
pub use bevy_utils::FxHasher as ReflectHasher;

/// An immutable enumeration of "kinds" of reflected type.
///
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_render/src/camera/camera_driver_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ impl Node for CameraDriverNode {
std::cmp::Ordering::Equal => t1.cmp(t2),
ord => ord,
});
let mut camera_windows = HashSet::new();
let mut camera_windows = HashSet::default();
let mut previous_order_target = None;
let mut ambiguities = HashSet::new();
let mut ambiguities = HashSet::default();
for (entity, order, target) in sorted_cameras {
let new_order_target = (order, target);
if let Some(previous_order_target) = previous_order_target {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_scene/src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
where
A: MapAccess<'de>,
{
let mut added = HashSet::new();
let mut added = HashSet::default();
let mut components = Vec::new();
while let Some(BorrowableCowStr(key)) = map.next_key()? {
if !added.insert(key.clone()) {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"]

[dependencies]
ahash = "0.7.0"
rustc-hash = "1.1"
tracing = { version = "0.1", default-features = false, features = ["std"] }
instant = { version = "0.1", features = ["wasm-bindgen"] }
uuid = { version = "1.1", features = ["v4", "serde"] }
Expand Down
50 changes: 19 additions & 31 deletions crates/bevy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ pub mod synccell;
mod default;
mod float_ord;

pub use ahash::AHasher;
pub use default::default;
pub use float_ord::*;
pub use hashbrown;
pub use instant::{Duration, Instant};
pub use rustc_hash::FxHasher;
pub use tracing;
pub use uuid::Uuid;

use ahash::RandomState;
use hashbrown::hash_map::RawEntryMut;
use std::{
fmt::Debug,
Expand All @@ -46,53 +45,42 @@ pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;

/// A shortcut alias for [`hashbrown::hash_map::Entry`].
pub type Entry<'a, K, V> = hashbrown::hash_map::Entry<'a, K, V, RandomState>;
pub type Entry<'a, K, V> = hashbrown::hash_map::Entry<'a, K, V, FixedState>;

/// A hasher builder that will create a fixed hasher.
#[derive(Debug, Clone, Default)]
pub struct FixedState;

impl std::hash::BuildHasher for FixedState {
type Hasher = AHasher;
type Hasher = FxHasher;

#[inline]
fn build_hasher(&self) -> AHasher {
AHasher::new_with_keys(
0b1001010111101110000001001100010000000011001001101011001001111000,
0b1100111101101011011110001011010100000100001111100011010011010101,
)
fn build_hasher(&self) -> Self::Hasher {
FxHasher::default()
}
}

/// A [`HashMap`][hashbrown::HashMap] implementing aHash, a high
/// speed keyed hashing algorithm intended for use in in-memory hashmaps.
///
/// aHash is designed for performance and is NOT cryptographically secure.
pub type HashMap<K, V> = hashbrown::HashMap<K, V, RandomState>;

/// A stable hash map implementing aHash, a high speed keyed hashing algorithm
/// intended for use in in-memory hashmaps.
///
/// Unlike [`HashMap`] this has an iteration order that only depends on the order
/// of insertions and deletions and not a random source.
///
/// aHash is designed for performance and is NOT cryptographically secure.
pub type StableHashMap<K, V> = hashbrown::HashMap<K, V, FixedState>;

/// A [`HashSet`][hashbrown::HashSet] implementing aHash, a high
/// speed keyed hashing algorithm intended for use in in-memory hashmaps.
/// FxHash is designed for performance and is NOT cryptographically secure.
/// It is also not [`HashDOS`] resistant, so exposing these maps to external
/// untrusted inputs is strongly discouraged.
///
/// aHash is designed for performance and is NOT cryptographically secure.
pub type HashSet<K> = hashbrown::HashSet<K, RandomState>;
/// As the initialization state is always the same, the iteration order only
/// depends on the order of insertionts and deletions and not a random source.
pub type HashMap<K, V> = hashbrown::HashMap<K, V, FixedState>;

/// A stable hash set implementing aHash, a high speed keyed hashing algorithm
/// intended for use in in-memory hashmaps.
/// A [`HashSet`][hashbrown::HashSet] implementing FxHash, a high
/// speed hashing algorithm intended for use in in-memory hashmaps.
///
/// Unlike [`HashSet`] this has an iteration order that only depends on the order
/// of insertions and deletions and not a random source.
/// FxHash is designed for performance and is NOT cryptographically secure.
/// It is also not [`HashDOS`] resistant, so exposing these sets to external
/// untrusted inputs is strongly discouraged.
///
/// aHash is designed for performance and is NOT cryptographically secure.
pub type StableHashSet<K> = hashbrown::HashSet<K, FixedState>;
/// As the initialization state is always the same, the iteration order only
/// depends on the order of insertionts and deletions and not a random source.
pub type HashSet<K> = hashbrown::HashSet<K, FixedState>;

/// A pre-hashed value of a specific type. Pre-hashing enables memoization of hashes that are expensive to compute.
/// It also enables faster [`PartialEq`] comparisons by short circuiting on hash equality.
Expand Down