Skip to content
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

Skip rehashing TypeIds #11268

Merged
merged 4 commits into from
Jan 13, 2024

Conversation

SpecificProtagonist
Copy link
Contributor

@SpecificProtagonist SpecificProtagonist commented Jan 9, 2024

Objective

TypeId contains a high-quality hash. Whenever a lookup based on a TypeId is performed (e.g. to insert/remove components), the hash is run through a second hash function. This is unnecessary.

Solution

Skip re-hashing TypeIds.

In my testing, this improves lookup performance consistently by 10%-15% (of course, the lookup is only a small part of e.g. a bundle insertion).

@tim-blackbird tim-blackbird added A-ECS Entities, components, systems, and events C-Performance A change motivated by improving speed, memory usage or compile times labels Jan 9, 2024
@NthTensor
Copy link
Contributor

Would it be better to panic if the behavior changes, instead of silently switching to a low quality fallback?

I think there might be a few other places where this would be useful. Maybe this should go in 'bevy_util' instead?

Comment on lines 70 to 75
// This will never be called: TypeId always just calls write_u64 once!
// This is unlikely to ever change, but as it isn't officially guaranteed,
// here's a fallback (which can be very low quality).
self.0 = bytes.iter().fold(self.0, |hash, b| {
hash.rotate_left(8).wrapping_add(*b as u64)
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on how likely this is to occur it might be more beneficial to panic or to emit a warning message if this happens.

Copy link
Contributor Author

@SpecificProtagonist SpecificProtagonist Jan 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty ambivalent about what's best here. An unimplemented!("explanation here") would probably be fine. A compile time warning would be ideal, but I don't see a reasonable way to do that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer unimplemented here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to unimplemented.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think unimplemented! is the right panic to use here. If this will never get called then unreachable! seems like the better choice.

@SpecificProtagonist
Copy link
Contributor Author

SpecificProtagonist commented Jan 9, 2024

I think there might be a few other places where this would be useful. Maybe this should go in 'bevy_util' instead?

Possibly, but that's orthogonal to this PR and can just be a followup PR.

@ghost
Copy link

ghost commented Jan 9, 2024

I think there might be a few other places where this would be useful. Maybe this should go in 'bevy_util' instead?

+1

@alice-i-cecile
Copy link
Member

Yeah I'm fine to move this later if we find downstream consumers (perhaps bevy_reflect).

@NthTensor
Copy link
Contributor

... if we find upstream consumers ...

I will probably make a follow up to this if #11228 is approved. It makes heavy use of type id hashmaps.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Jan 13, 2024
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Jan 13, 2024
Merged via the queue into bevyengine:main with commit 69760c7 Jan 13, 2024
23 checks passed
@orzogc
Copy link
Contributor

orzogc commented Jan 13, 2024

This PR relies on Rust std/core lib's internal implement. IMO we should have a last resort when TypeId's hashing behaviour changed.
Maybe we could use build.rs to check whether TypeId only hashes itself as u64 once and set cfg to switch hasher at compile-time.

@SpecificProtagonist
Copy link
Contributor Author

SpecificProtagonist commented Jan 13, 2024

This PR relies on Rust std/core lib's internal implement. IMO we should have a last resort when TypeId's hashing behaviour changed.

Note that Bevy isn't the only crate that relies on the write_u64 call. And from the discussion about the current implementation of TypeId:

The use of ::hash to extract a sufficiently hashed/mixed u64 identifier through Hasher::hash_u64 is an informally pseudo-blessed pattern for this. Merging this PR still doesn't officially endorse this, but it does add to the implicit acknowledgement that this is a legitimate pattern, since we're deliberately not breaking it.

The libs team deliberately does not endorse doing this, but as they're aware of this in the unlikely (other Hash impls were already discussed for this) case the Hash impl changes, there should be another crater run (additionally, bevy_ecs is tested under nightly miri).

Maybe we could use build.rs to check whether TypeId only hashes itself as u64 once and set cfg to switch hasher at compile-time.

I'm not sure that's worth it, build.rs is stuff can be a bit messy. An alternative I'd be happy with would be to add back in the write implementation from before I changed to a panic on request (or copy FxHasher's write) and add a test that checks that it isn't called 🙂

@alice-i-cecile
Copy link
Member

An alternative I'd be happy with would be to add back in the write implementation from before I changed to a panic on request (or copy FxHasher's write) and add a test that checks that it isn't called 🙂

This seems like a good path forward.

github-merge-queue bot pushed a commit that referenced this pull request Jan 14, 2024
…1334)

Based on discussion after #11268 was merged:
Instead of panicking should the impl of `TypeId::hash` change
significantly, have a fallback and detect this in a test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Performance A change motivated by improving speed, memory usage or compile times S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants