bevy_ecs/entity/hash.rs
1use core::hash::{BuildHasher, Hasher};
2
3#[cfg(feature = "bevy_reflect")]
4use bevy_reflect::Reflect;
5use bevy_utils::hashbrown;
6
7use super::Entity;
8
9/// A [`BuildHasher`] that results in a [`EntityHasher`].
10#[derive(Default, Clone)]
11#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
12pub struct EntityHash;
13
14impl BuildHasher for EntityHash {
15 type Hasher = EntityHasher;
16
17 fn build_hasher(&self) -> Self::Hasher {
18 Self::Hasher::default()
19 }
20}
21
22/// A very fast hash that is only designed to work on generational indices
23/// like [`Entity`]. It will panic if attempting to hash a type containing
24/// non-u64 fields.
25///
26/// This is heavily optimized for typical cases, where you have mostly live
27/// entities, and works particularly well for contiguous indices.
28///
29/// If you have an unusual case -- say all your indices are multiples of 256
30/// or most of the entities are dead generations -- then you might want also to
31/// try [`AHasher`](bevy_utils::AHasher) for a slower hash computation but fewer lookup conflicts.
32#[derive(Debug, Default)]
33pub struct EntityHasher {
34 hash: u64,
35}
36
37impl Hasher for EntityHasher {
38 #[inline]
39 fn finish(&self) -> u64 {
40 self.hash
41 }
42
43 fn write(&mut self, _bytes: &[u8]) {
44 panic!("EntityHasher can only hash u64 fields.");
45 }
46
47 #[inline]
48 fn write_u64(&mut self, bits: u64) {
49 // SwissTable (and thus `hashbrown`) cares about two things from the hash:
50 // - H1: low bits (masked by `2ⁿ-1`) to pick the slot in which to store the item
51 // - H2: high 7 bits are used to SIMD optimize hash collision probing
52 // For more see <https://abseil.io/about/design/swisstables#metadata-layout>
53
54 // This hash function assumes that the entity ids are still well-distributed,
55 // so for H1 leaves the entity id alone in the low bits so that id locality
56 // will also give memory locality for things spawned together.
57 // For H2, take advantage of the fact that while multiplication doesn't
58 // spread entropy to the low bits, it's incredibly good at spreading it
59 // upward, which is exactly where we need it the most.
60
61 // While this does include the generation in the output, it doesn't do so
62 // *usefully*. H1 won't care until you have over 3 billion entities in
63 // the table, and H2 won't care until something hits generation 33 million.
64 // Thus the comment suggesting that this is best for live entities,
65 // where there won't be generation conflicts where it would matter.
66
67 // The high 32 bits of this are ⅟φ for Fibonacci hashing. That works
68 // particularly well for hashing for the same reason as described in
69 // <https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/>
70 // It loses no information because it has a modular inverse.
71 // (Specifically, `0x144c_bc89_u32 * 0x9e37_79b9_u32 == 1`.)
72 //
73 // The low 32 bits make that part of the just product a pass-through.
74 const UPPER_PHI: u64 = 0x9e37_79b9_0000_0001;
75
76 // This is `(MAGIC * index + generation) << 32 + index`, in a single instruction.
77 self.hash = bits.wrapping_mul(UPPER_PHI);
78 }
79}
80
81/// A [`HashMap`](hashbrown::HashMap) pre-configured to use [`EntityHash`] hashing.
82pub type EntityHashMap<V> = hashbrown::HashMap<Entity, V, EntityHash>;
83
84/// A [`HashSet`](hashbrown::HashSet) pre-configured to use [`EntityHash`] hashing.
85pub type EntityHashSet = hashbrown::HashSet<Entity, EntityHash>;
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use static_assertions::assert_impl_all;
91
92 // Check that the HashMaps are Clone if the key/values are Clone
93 assert_impl_all!(EntityHashMap::<usize>: Clone);
94 // EntityHashMap should implement Reflect
95 #[cfg(feature = "bevy_reflect")]
96 assert_impl_all!(EntityHashMap::<i32>: Reflect);
97}