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

[Merged by Bors] - Fix size_hint for partially consumed QueryIter and QueryCombinationIter #5214

Closed
wants to merge 4 commits into from

Conversation

nicopap
Copy link
Contributor

@nicopap nicopap commented Jul 5, 2022

Objective

Fix #5149

Solution

Instead of returning the total count of elements in the QueryIter in
size_hint, we return the count of remaining elements. This
Fixes #5149 even when #5148 gets merged.


Changelog

  • Fix partially consumed QueryIter and QueryCombinationIter having invalid size_hint

@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior A-ECS Entities, components, systems, and events labels Jul 5, 2022
@nicopap nicopap force-pushed the fix-exactsize branch 2 times, most recently from ee4a84b to c94a1b3 Compare July 5, 2022 16:16
@nicopap nicopap changed the title Fix size_hint for partially consumed QueryIter Fix size_hint for partially consumed QueryIter and QueryCombinationIter Jul 5, 2022
@nicopap
Copy link
Contributor Author

nicopap commented Jul 5, 2022

We already keep track of which items we iterated over in the QueryIterationCursor, so we might as well use it to compute how many elements there are to iterate over instead of using the full archetype list. It basically cost the same as the current method.

For QueryCombinationIter, we do K binomial coefficients rather than a single one. But I think it's fine, since for each step, we substract 1 from K and N. Also the base case of non-consumed iterator also stays the same as before.

Comment on lines 335 to 421
fn choose(n: usize, k: usize) -> Option<usize> {
if k > n || n == 0 {
return Some(0);
}
let k = k.min(n - k);
let ks = 1..=k;
let ns = (n + 1 - k..=n).rev();
ks.zip(ns)
.try_fold(1_usize, |acc, (k, n)| Some(acc.checked_mul(n)? / k))
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same comment as #5148 (comment)

The checked_mul might be overdoing it though, it makes the code harder to read for little benefit

@alice-i-cecile
Copy link
Member

@nicopap can you update this to reflect the changes that are about to be merged in #5148? Then we can clean up the behavior everywhere at once.

@nicopap
Copy link
Contributor Author

nicopap commented Jul 13, 2022

@alice-i-cecile Done now. Should be good to go.

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

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

Logic and tests make sense to me. My understanding is that this is off the iteration hot-path, so we can get away with doing the right thing here.

Can you run a before/after on the iteration benchmarks to double-check that nothing funny is happening?

@james7132 I want your opinion on this.

@nicopap
Copy link
Contributor Author

nicopap commented Jul 14, 2022

Note that ExactSizeIterator is not that much of a performance win. collect used the minimum bound of size_hint already for pre-allocating. It's just (but still pretty good) an ergonomic win. This still doesn't enable optimizing away checks for reallocation (even if we know it will never happen) because it's safe API. TrustedLen could really be helpful here, and the way overflow is handled in the combination code is compliant with the TrustedLen API. However, TrustedLen is still unstable and we cannot use it.

@nicopap
Copy link
Contributor Author

nicopap commented Jul 14, 2022

Here are the criterion results https://nicopap.ch/p/bevy_exact_size_iter_criterion_results/all_changes.html

The benchmark ran on a very clean machine with a minimal amount of concurrent processes. I've noticed they were fairly consistent with regard to reproducibility. But multithreading seems to be very noisy on my machine, no idea why.

@alice-i-cecile
Copy link
Member

Thanks for the criterion results. I'm happy to call that a wash; multithreading is noisy and there's no critical regressions.

@hymm
Copy link
Contributor

hymm commented Jul 21, 2022

Here are the criterion results https://nicopap.ch/p/bevy_exact_size_iter_criterion_results/all_changes.html

I don't think any of these benches shown above use the query iterator. Do you have results for the benches in this folder? https://github.com/bevyengine/bevy/tree/main/benches/benches/bevy_ecs/iteration

edit: I also don't think we have any benches for iter combinations, which I would be more worried about the performance of.

@nicopap
Copy link
Contributor Author

nicopap commented Jul 22, 2022

I don't think any of these benches shown above use the query iterator. Do you have results for the benches in this folder? https://github.com/bevyengine/bevy/tree/main/benches/benches/bevy_ecs/iteration

Wow my bad! I updated the page with the actual ECS iteration benches. The bench are a bit more consistent.

It seems I did the benches for the bevy_tasks/iter.rs previously. It's very surprising that a change that supposedly has nothing related would result in bench differences of up to 30% (in both direction). Wouldn't that make the benches unreliable? Would it not be better to delete them altogether if they cannot be trusted?

@cart cart removed this from the Bevy 0.8 milestone Jul 30, 2022
nicopap and others added 3 commits November 1, 2022 11:55
Instead of returning the total count of elements in the `QueryIter` in
`size_hint`, we return the count of remaining elements in it. This
Fixes bevyengine#5149. This is also true of `QueryCombinationIter`.

- bevyengine#5149
- bevyengine#5148
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
@@ -57,100 +57,6 @@ mod tests {

#[test]
fn query_filtered_exactsizeiterator_len() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that this test was redundant with query_filtered_combination_size because we merged the two, and is why it's deleted.

Copy link
Member

@james7132 james7132 left a comment

Choose a reason for hiding this comment

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

Took a bit to review the math, but LGTM.

crates/bevy_ecs/src/query/iter.rs Outdated Show resolved Hide resolved
@james7132 james7132 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 Nov 15, 2022
@james7132 james7132 added this to the 0.9.1 milestone Nov 15, 2022
Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

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

bors r+

bors bot pushed a commit that referenced this pull request Nov 21, 2022
…er (#5214)

# Objective

Fix #5149

## Solution

Instead of returning the **total count** of elements in the `QueryIter` in
`size_hint`, we return the **count of remaining elements**. This
Fixes #5149 even when #5148 gets merged.

- #5149
- #5148

---

## Changelog

- Fix partially consumed `QueryIter` and `QueryCombinationIter` having invalid `size_hint`


Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
@bors bors bot changed the title Fix size_hint for partially consumed QueryIter and QueryCombinationIter [Merged by Bors] - Fix size_hint for partially consumed QueryIter and QueryCombinationIter Nov 21, 2022
@bors bors bot closed this Nov 21, 2022
cart pushed a commit that referenced this pull request Nov 30, 2022
…er (#5214)

# Objective

Fix #5149

## Solution

Instead of returning the **total count** of elements in the `QueryIter` in
`size_hint`, we return the **count of remaining elements**. This
Fixes #5149 even when #5148 gets merged.

- #5149
- #5148

---

## Changelog

- Fix partially consumed `QueryIter` and `QueryCombinationIter` having invalid `size_hint`


Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
…er (bevyengine#5214)

# Objective

Fix bevyengine#5149

## Solution

Instead of returning the **total count** of elements in the `QueryIter` in
`size_hint`, we return the **count of remaining elements**. This
Fixes bevyengine#5149 even when bevyengine#5148 gets merged.

- bevyengine#5149
- bevyengine#5148

---

## Changelog

- Fix partially consumed `QueryIter` and `QueryCombinationIter` having invalid `size_hint`


Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
@nicopap nicopap deleted the fix-exactsize branch September 5, 2023 15:47
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-Bug An unexpected or incorrect behavior 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.

Query's ExactSizeIterator does not account for partial iteration
6 participants