diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index e0bbd568afd80..0723204d70dd0 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -115,9 +115,17 @@ type IdCursor = isize; /// [`EntityCommands`]: crate::system::EntityCommands /// [`Query::get`]: crate::system::Query::get /// [`World`]: crate::world::World -#[derive(Clone, Copy, Eq, Ord, PartialOrd)] +#[derive(Clone, Copy)] +// Alignment repr necessary to allow LLVM to better output +// optimised codegen for `to_bits`, `PartialEq` and `Ord`. +#[repr(C, align(8))] pub struct Entity { + // Do not reorder the fields here. The ordering is explicitly used by repr(C) + // to make this struct equivalent to a u64. + #[cfg(target_endian = "little")] + index: u32, generation: u32, + #[cfg(target_endian = "big")] index: u32, } @@ -126,7 +134,42 @@ pub struct Entity { impl PartialEq for Entity { #[inline] fn eq(&self, other: &Entity) -> bool { - (self.generation == other.generation) & (self.index == other.index) + // By using `to_bits`, the codegen can be optimised out even + // further potentially. Relies on the correct alignment/field + // order of `Entity`. + self.to_bits() == other.to_bits() + } +} + +impl Eq for Entity {} + +// The derive macro codegen output is not optimal and can't be optimised as well +// by the compiler. This impl resolves the issue of non-optimal codegen by relying +// on comparing against the bit representation of `Entity` instead of comparing +// the fields. The result is then LLVM is able to optimise the codegen for Entity +// far beyond what the derive macro can. +// See +impl PartialOrd for Entity { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + // Make use of our `Ord` impl to ensure optimal codegen output + Some(self.cmp(other)) + } +} + +// The derive macro codegen output is not optimal and can't be optimised as well +// by the compiler. This impl resolves the issue of non-optimal codegen by relying +// on comparing against the bit representation of `Entity` instead of comparing +// the fields. The result is then LLVM is able to optimise the codegen for Entity +// far beyond what the derive macro can. +// See +impl Ord for Entity { + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + // This will result in better codegen for ordering comparisons, plus + // avoids pitfalls with regards to macro codegen relying on property + // position when we want to compare against the bit representation. + self.to_bits().cmp(&other.to_bits()) } } @@ -197,6 +240,7 @@ impl Entity { /// In general, one should not try to synchronize the ECS by attempting to ensure that /// `Entity` lines up between instances, but instead insert a secondary identifier as /// a component. + #[inline] pub const fn from_raw(index: u32) -> Entity { Entity { index, @@ -210,6 +254,7 @@ impl Entity { /// for serialization between runs. /// /// No particular structure is guaranteed for the returned bits. + #[inline(always)] pub const fn to_bits(self) -> u64 { (self.generation as u64) << 32 | self.index as u64 } @@ -217,6 +262,7 @@ impl Entity { /// Reconstruct an `Entity` previously destructured with [`Entity::to_bits`]. /// /// Only useful when applied to results from `to_bits` in the same instance of an application. + #[inline(always)] pub const fn from_bits(bits: u64) -> Self { Self { generation: (bits >> 32) as u32,