-
-
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
Switch usage of std HashMap/HashSet default hasher, to aHash algo #258
Conversation
@@ -88,7 +88,7 @@ impl TextureAtlasBuilder { | |||
rect_placements = None; | |||
break; | |||
} | |||
let mut target_bins = HashMap::new(); | |||
let mut target_bins = std::collections::HashMap::new(); |
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.
Just looked at this for fun, and the new 0.2 version of rectangle_pack actually uses BTreeMap here to behave more deterministically.
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.
nice find - perhaps a new issue for updating the version of rectangle_pack ?
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.
Just looked at, and using a BTree requires a bunch of Stuff to impl Ord, like Texture, which contains a Vec2 with f32s. Definitely doable, but a bit more involved.
I'm curios if there is any runtime performance difference between using hashbrown and ahash directly with the std HashMap. Since the latter is actually using hashbrown under the hood I'd expect it to perform the same while being faster to compile. |
Might be a good idea to include benchmarks of how this change impacts bevy compile time and bevy runtime. I understand in the linked repo's benchmarks it looks good but I personally think it's important to have benchmarks in context, otherwise we can't really be sure. |
Love the conversation happening here. Its worth doing some due diligence here to make sure we made the right call. I gave this a quick run and its already showing a nice performance improvement on the Before this change, we get ~16.5 fps. After this change, we get about ~20.1 fps. |
If someone could run some compile time benchmarks that would be great. I really like @killercup's idea to try using aHash directly. We should then evaluate compile times and runtimes again. |
I'm sure there's a better way to test this but here's what I did anyway. Did a full clean-build cycle three times on each of the following: hashbrown full-build: 2m09s, 2m04s, 2m01s So yes excluding hash-brown will definitely speed up compile times. I'll go back and redo it to try using the std::collection variants with the ahasher and post results. Edit updates: Interestingly there doesn't seem to be that much of a difference, we're talking 1-2 seconds between [Everything hashbrown], [everything std + hashbrown ecs] and [everything std with aHasher]. Although I am curious as to why builds randomly take 3-4 seconds longer than the same one. Perhaps someone with a better testing methodology could check it out? Rough FPS for spawner example in release. I took the first 10 outputs took the average. |
use std with ahash
…into switchHashAlgoToAHash
From my comment above, I posted my findings for using std::collections hash map + hash set with the aHash hasher instead. Looks like compile times are almost identical. Interestingly, performance seems to be slightly more improved using the std+ahasher route compared to just hashbrown default. Either way, I don't think my testing methods were ideal so hope someone else could chime in! The current PR is now going with the std+ahash route. |
tl;dr Sounds like going ahash has no downsides in this case! Nice! Did a quick compile time check myself and Going the ahash route also makes it possible to interact with (well-written) libraries expecting std HashMaps, btw.
That is surprising me as well! I don't think the std lib version has additional inlining or optimizations applied to it. |
Thanks @RobDavenport and @killercup for the thoroughness. Sounds like std+aHash is the way to go. @RobDavenport, can you resolve the merge conflicts? Once thats done I'll take one more sanity pass over the changes and I think we'll be good to go. |
@cart Fixed the merge conflicts, mostly just conflicting cargo.toml files with the new inclusion of parking_lot to many of the crates. I ran a full rebuild + the spawner example and seemed to work well. Should be good to go from now! |
Alrighty one more thought to simplify this a little bit: HashMaps are common enough that making them easy to consume / consistent is worth it. I think we should export the following type alias from bevy_core::collections: pub type HashMap<K, V> = HashMap<K, V, RandomState>; This removes the need to add aHash dependencies everywhere and simplifies type declarations. |
(and lets do the same for HashSet) |
@cart, I tried exposing the HashMap type as a part of bevy_core, but unfortunately I ran into some cyclical dependencies. I'm really not familiar with rust's build system yet so I wasn't able to find a good way to solve this. I had it setup like so
And then modified bevy_core/src/lib.rs to include the collections module, and expose it with pub use.
This was triggered by adding the |
Hm, yeah, you'd have to add another crate (bevy_common or something like that) to resolve that cycle. I'm wondering, though: Why not use this type def from the ahash crate? |
@RobDavenport Arg yeah thats my bad. Sorry! I like @killercup's idea to use the typedef from the ahash crate. Lets just do that for now. |
@cart @killercup are you suggesting to change the instances of std::collections::hashmap to use ahash::AHashMap instead? Wont this still cause the problem of having to include ahash dependencies everywhere? Edit: I went ahead and tried changing it, but ran into another problem, this time it's due to the impl_property! macro which requires Serialize + Deserialize traits for the key + value for AHashMap. I found this issue on the aHash repo tkaitchuck/aHash#42. |
@RobDavenport I would try using |
@killercup did you see my edit? You replied around the same time I posted it |
You're right, I didn't see it yet :) Reading the issue, I guess it's a wrapper to be able to overwrite the constructor methods for the hash map. Can you use |
Okay, just tried this for a bit and there are a lot of places where it's easy to switch to ahash, but some are more tricky; like macros referring to HashMaps and the whole properties thing. I am not involved in this project and was mostly just curios. From my experience with Rust projects and I'd recommedn this:
|
@killercup thanks for looking into it. Personally I'd prefer to just put the ahashmaps everywhere just to keep things clean and identical everywhere, but looks like it wont be such an easy task for now. @cart What do you recommend from here? |
If its not too much trouble, lets do a new |
@cart I followed your advice and created the utils crate. Please take a look when you have time! |
Looks good to me! Thanks for dealing with the churn as we landed on the best approach. This is a huge win! |
…vyengine#258) switch to ahash for HashMaps and HashSets via a new bevy_utils crate
First time submitting a PR to OSS!
For #157.
Using the benchmarks seen from https://github.com/tkaitchuck/aHash, I just went ahead and changed all instances of std HashMap and HashSet to use hashbrown's versions. This is a drop-in replacement, since hashbrown default's to ahash, we are able to get the performance benefits without any major refactoring. The aHash algos should be faster for all of the current uses in Bevy.
Some points of interest:
crates/bevy_ecs/hecs/macros/src/lib.rs
to also use a hashbrown hashset instead of std::collections::HashSet. I'm not sure if this was a good idea or not. Honestly I'm not too familiar with macros in Rust so would appreciate some special attention there. Hopefully I used the#path
thing correctly.I ran a few examples (including the breakout game) and seems to run OK.