mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 22:20:20 +00:00
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](https://github.com/bevyengine/bevy/issues/1097) [Engine wide benchmarks](https://github.com/bevyengine/bevy/pull/1119#issuecomment-751361215)
This commit is contained in:
parent
0e0906d22e
commit
9bce8712b5
2 changed files with 6 additions and 43 deletions
|
@ -20,6 +20,7 @@ trace = []
|
|||
bevy_tasks = { path = "../bevy_tasks", version = "0.4.0" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.4.0" }
|
||||
bevy_ecs_macros = { path = "macros", version = "0.4.0" }
|
||||
fxhash = "0.2"
|
||||
rand = "0.7.3"
|
||||
serde = "1.0"
|
||||
thiserror = "1.0"
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
// modified by Bevy contributors
|
||||
|
||||
use crate::{AtomicBorrow, Component, Entity};
|
||||
use bevy_utils::AHasher;
|
||||
use bitflags::bitflags;
|
||||
use std::{
|
||||
alloc::{alloc, dealloc, Layout},
|
||||
any::{type_name, TypeId},
|
||||
cell::UnsafeCell,
|
||||
collections::HashMap,
|
||||
hash::{BuildHasherDefault, Hasher},
|
||||
mem,
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
|
@ -557,44 +555,8 @@ fn align(x: usize, alignment: usize) -> usize {
|
|||
|
||||
/// A hasher optimized for hashing a single TypeId.
|
||||
///
|
||||
/// TypeId is already thoroughly hashed, so there's no reason to hash it again.
|
||||
/// Just leave the bits unchanged.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TypeIdHasher {
|
||||
hash: u64,
|
||||
}
|
||||
|
||||
impl Hasher for TypeIdHasher {
|
||||
fn write_u64(&mut self, n: u64) {
|
||||
// Only a single value can be hashed, so the old hash should be zero.
|
||||
debug_assert_eq!(self.hash, 0);
|
||||
self.hash = n;
|
||||
}
|
||||
|
||||
// Tolerate TypeId being either u64 or u128.
|
||||
fn write_u128(&mut self, n: u128) {
|
||||
debug_assert_eq!(self.hash, 0);
|
||||
self.hash = n as u64;
|
||||
}
|
||||
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
debug_assert_eq!(self.hash, 0);
|
||||
|
||||
// This will only be called if TypeId is neither u64 nor u128, which is not anticipated.
|
||||
// In that case we'll just fall back to using a different hash implementation.
|
||||
let mut hasher = AHasher::default();
|
||||
hasher.write(bytes);
|
||||
self.hash = hasher.finish();
|
||||
}
|
||||
|
||||
fn finish(&self) -> u64 {
|
||||
self.hash
|
||||
}
|
||||
}
|
||||
|
||||
/// A HashMap with TypeId keys
|
||||
///
|
||||
/// Because TypeId is already a fully-hashed u64 (including data in the high seven bits,
|
||||
/// which hashbrown needs), there is no need to hash it again. Instead, this uses the much
|
||||
/// faster no-op hash.
|
||||
pub(crate) type TypeIdMap<V> = HashMap<TypeId, V, BuildHasherDefault<TypeIdHasher>>;
|
||||
/// We don't use RandomState from std or Random state from Ahash
|
||||
/// because fxhash is [proved to be faster](https://github.com/bevyengine/bevy/pull/1119#issuecomment-751361215)
|
||||
/// and we don't need Hash Dos attack protection here
|
||||
/// since TypeIds generated during compilation and there is no reason to user attack himself.
|
||||
pub(crate) type TypeIdMap<V> = HashMap<TypeId, V, fxhash::FxBuildHasher>;
|
||||
|
|
Loading…
Reference in a new issue