Skip to content

Commit 9bce871

Browse files
Use fxhash in TypeIdMap. (#1119)
Relying on TypeId being some hash internally isn't future-proof because there is no guarantee about internal layout or structure of TypeId. I benchmarked TypeId noop hasher vs fxhash and found that there is very little difference. Also fxhash is likely to be better supported because it is widely used in rustc itself. [Benchmarks of hashers](#1097) [Engine wide benchmarks](#1119 (comment))
1 parent 0e0906d commit 9bce871

File tree

2 files changed

+6
-43
lines changed

2 files changed

+6
-43
lines changed

crates/bevy_ecs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ trace = []
2020
bevy_tasks = { path = "../bevy_tasks", version = "0.4.0" }
2121
bevy_utils = { path = "../bevy_utils", version = "0.4.0" }
2222
bevy_ecs_macros = { path = "macros", version = "0.4.0" }
23+
fxhash = "0.2"
2324
rand = "0.7.3"
2425
serde = "1.0"
2526
thiserror = "1.0"

crates/bevy_ecs/src/core/archetype.rs

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@
1515
// modified by Bevy contributors
1616

1717
use crate::{AtomicBorrow, Component, Entity};
18-
use bevy_utils::AHasher;
1918
use bitflags::bitflags;
2019
use std::{
2120
alloc::{alloc, dealloc, Layout},
2221
any::{type_name, TypeId},
2322
cell::UnsafeCell,
2423
collections::HashMap,
25-
hash::{BuildHasherDefault, Hasher},
2624
mem,
2725
ptr::{self, NonNull},
2826
};
@@ -557,44 +555,8 @@ fn align(x: usize, alignment: usize) -> usize {
557555

558556
/// A hasher optimized for hashing a single TypeId.
559557
///
560-
/// TypeId is already thoroughly hashed, so there's no reason to hash it again.
561-
/// Just leave the bits unchanged.
562-
#[derive(Default)]
563-
pub(crate) struct TypeIdHasher {
564-
hash: u64,
565-
}
566-
567-
impl Hasher for TypeIdHasher {
568-
fn write_u64(&mut self, n: u64) {
569-
// Only a single value can be hashed, so the old hash should be zero.
570-
debug_assert_eq!(self.hash, 0);
571-
self.hash = n;
572-
}
573-
574-
// Tolerate TypeId being either u64 or u128.
575-
fn write_u128(&mut self, n: u128) {
576-
debug_assert_eq!(self.hash, 0);
577-
self.hash = n as u64;
578-
}
579-
580-
fn write(&mut self, bytes: &[u8]) {
581-
debug_assert_eq!(self.hash, 0);
582-
583-
// This will only be called if TypeId is neither u64 nor u128, which is not anticipated.
584-
// In that case we'll just fall back to using a different hash implementation.
585-
let mut hasher = AHasher::default();
586-
hasher.write(bytes);
587-
self.hash = hasher.finish();
588-
}
589-
590-
fn finish(&self) -> u64 {
591-
self.hash
592-
}
593-
}
594-
595-
/// A HashMap with TypeId keys
596-
///
597-
/// Because TypeId is already a fully-hashed u64 (including data in the high seven bits,
598-
/// which hashbrown needs), there is no need to hash it again. Instead, this uses the much
599-
/// faster no-op hash.
600-
pub(crate) type TypeIdMap<V> = HashMap<TypeId, V, BuildHasherDefault<TypeIdHasher>>;
558+
/// We don't use RandomState from std or Random state from Ahash
559+
/// because fxhash is [proved to be faster](https://github.com/bevyengine/bevy/pull/1119#issuecomment-751361215)
560+
/// and we don't need Hash Dos attack protection here
561+
/// since TypeIds generated during compilation and there is no reason to user attack himself.
562+
pub(crate) type TypeIdMap<V> = HashMap<TypeId, V, fxhash::FxBuildHasher>;

0 commit comments

Comments
 (0)