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

Add #[track_caller] to Query methods #12984

Merged
merged 1 commit into from
Apr 24, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/bevy_ecs/src/system/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// - [`get_many`](Self::get_many) for the non-panicking version.
#[inline]
#[track_caller]
Copy link
Member

Choose a reason for hiding this comment

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

Iirc track_caller does introduce some minor runtime overhead. For high frequency usage this might show up on benchmarks. Can we do a sanity check here with before / after benches.

Copy link
Member Author

Choose a reason for hiding this comment

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

I created a benchmark comparing the overhead of #[track_caller] on my M1 MacBook. (See the gist.) There appears to be little to no overhead whatsoever.

tracked false           time:   [1.5369 ns 1.5376 ns 1.5383 ns]
Found 8 outliers among 100 measurements (8.00%)
  5 (5.00%) high mild
  3 (3.00%) high severe

untracked false         time:   [1.5371 ns 1.5376 ns 1.5382 ns]
Found 5 outliers among 100 measurements (5.00%)
  3 (3.00%) high mild
  2 (2.00%) high severe

If you look at the average time, they both take 1.5376 nanoseconds. The #[track_caller] has a greater deviation, with both a lower minimum and greater maximum, but this is sub-nanosecond behavior.

Furthermore, you can compare the generated assembly here.

untracked:
  test edi, edi
  jne .LBB7_2
  ret
.LBB7_2: ; panicking code, no `#[track_caller]`
  push rax
  lea rdi, [rip + .L__unnamed_3] ; returns panic message
  lea rdx, [rip + .L__unnamed_4] ; different
  mov esi, 9
  call _ZN3std9panicking11begin_panic17he275593115e721e6E

tracked:
  test edi, edi
  jne .LBB8_2
  ret
.LBB8_2: ; panicking code, with `#[track_caller]`
  push rax
  mov rdx, rsi ; different
  lea rdi, [rip + .L__unnamed_5] ; returns panic message
  mov esi, 7
  call _ZN3std9panicking11begin_panic17he275593115e721e6E

The only difference in the generated code is a lea instruction being replaced with a mov instruction. This change is only noticeable when the function panics, where time is not an issue.

pub fn many<const N: usize>(&self, entities: [Entity; N]) -> [ROQueryItem<'_, D>; N] {
match self.get_many(entities) {
Ok(items) => items,
Expand Down Expand Up @@ -1038,6 +1039,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// - [`get_many_mut`](Self::get_many_mut) for the non panicking version.
/// - [`many`](Self::many) to get read-only query items.
#[inline]
#[track_caller]
pub fn many_mut<const N: usize>(&mut self, entities: [Entity; N]) -> [D::Item<'_>; N] {
match self.get_many_mut(entities) {
Ok(items) => items,
Expand Down Expand Up @@ -1351,6 +1353,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
///
/// [`EntityLocation`]: crate::entity::EntityLocation
/// [`&Archetype`]: crate::archetype::Archetype
#[track_caller]
pub fn transmute_lens<NewD: QueryData>(&mut self) -> QueryLens<'_, NewD> {
self.transmute_lens_filtered::<NewD, ()>()
}
Expand All @@ -1361,6 +1364,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
/// additional archetypal query terms like [`With`](crate::query::With) and [`Without`](crate::query::Without)
/// will not necessarily be respected and non-archetypal terms like [`Added`](crate::query::Added) and
/// [`Changed`](crate::query::Changed) will only be respected if they are in the type signature.
#[track_caller]
pub fn transmute_lens_filtered<NewD: QueryData, NewF: QueryFilter>(
&mut self,
) -> QueryLens<'_, NewD, NewF> {
Expand Down