-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Conversation
In the early days of Bevy I chose aHash over FXHash because it performed measurably better for small index-sized keys (this was back when we were using |
From a gut instinct, the only cases where we weren't using FxHash already is in reflection and assets, and by proxy rendering. The only major use in a hot location in ECS now is just Rendering, in particular, I don't expect large changes since the bottleneck lies elsewhere. |
I've noticed no change at all in code that is mutating 1_000_000 entities in 500 systems, if that means anything. rustc_hash is used by the rust compiler internally, it was specifically created for it's speed without relying on architecture specific instruction like AES. |
Adding some context from the discord discussion the other day around this: Gut check from reading the related material is that FxHash is faster for the act of hashing on small keys (not necessarily larger keys), whereas aHash is more likely to aid in avoiding collisions in storage and lookup. The difference between the two is minor and it's difficult to determine the best without profiling. Consolidating dependencies would also be good in its own right. If we do end up doing benchmark comparisons on this, might be worth throwing xxhash-rust into the mix. |
bors try |
tryBuild failed: |
I'm happy to merge this without further benchmarking based on @AxiomaticSemantics's experiments once CI is passing. |
I actually want to fully test this on some stress tests outside of pure ECS tests, especially for rendering, since the only real API heavily reliant on HashMaps in the ECS is World/EntityRef::get and friends, and those are already using FxHash. I just haven't had the time to run them. |
Finally had some time to dig into this. Compared this against On a frame level, there seems to be a ~2.9% (4.08ms -> 3.96ms) performance improvement. When looking at
These results are in line with the expectations in the aforementioned performance guidance about hashing (single digit percentage improvements in use cases bound by HashMap lookup. With that said, this is still focused primarily on a case where there's really only one entry in the HashMap, so it's probably benefiting from the faster hashing, but it's unclear if the quality of it's output will have a more pronounced impact on more varied scenes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those benchmarks have convinced me; feel free to merge once you pass CI.
Holding off from merging this since:
As it currently stands, that's probably not enough to justify merging this. |
Agreed. I'm going to close this out for now. Dear reader, feel free to revive this PR if you have a good argument for why it should be merged. |
# Objective We currently over/underpromise hash stability: - `HashMap`/`HashSet` use `BuildHasherDefault<AHasher>` instead of `RandomState`. As a result, the hash is stable within the same run. - [aHash isn't stable between devices (and versions)](https://github.com/tkaitchuck/ahash?tab=readme-ov-file#goals-and-non-goals), yet it's used for `StableHashMap`/`StableHashSet` - the specialized hashmaps are stable Interestingly, `StableHashMap`/`StableHashSet` aren't used by Bevy itself (anymore). ## Solution Add/fix documentation ## Alternatives For `StableHashMap`/`StableHashSet`: - remove them - revive #7107 --- ## Changelog - added iteration stability guarantees for different hashmaps
# Objective We currently over/underpromise hash stability: - `HashMap`/`HashSet` use `BuildHasherDefault<AHasher>` instead of `RandomState`. As a result, the hash is stable within the same run. - [aHash isn't stable between devices (and versions)](https://github.com/tkaitchuck/ahash?tab=readme-ov-file#goals-and-non-goals), yet it's used for `StableHashMap`/`StableHashSet` - the specialized hashmaps are stable Interestingly, `StableHashMap`/`StableHashSet` aren't used by Bevy itself (anymore). ## Solution Add/fix documentation ## Alternatives For `StableHashMap`/`StableHashSet`: - remove them - revive bevyengine#7107 --- ## Changelog - added iteration stability guarantees for different hashmaps
Objective
Speed up all
HashMap
queries in the engine. Shrink the dependency tree.Solution
Instead of using
aHash
as the default hasher for all of the engine's HashMap and HashSets, useFxHash
instead. This collapsesStableHashMap
andStableHashSet
into the normalHashMap
/HashSet
.This also removes the
fxhash
dependency onbevy_ecs
, instead havingbevy_utils
depend onrustc-hash
instead, which is better maintained, hit 1.0 already, and has zero dependencies.This does mean that none of the types should now be exposed to external untrusted inputs (i.e. networking), since FxHash is not HashDOS resistant.
Performance
From an external source, performance could increase as much as 4% in HashMap bound code.
Changelog
Removed:
bevy::utils::AHash
.Removed:
bevy::utils::StableHashMap
.Removed:
bevy::utils::StableHashSet
.Migration Guide
If you were relying on
bevy::utils::StableHashMap
orbevy::utils::StableHashSet
, usebevy::utils::HashMap
andbevy::utils::HashSet
instead. The default hash map and set now have a stable iteration order.