Skip to content

[3.x] Fast child iteration in Node, Spatial, CanvasItem #107702

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

Open
wants to merge 1 commit into
base: 3.x
Choose a base branch
from

Conversation

lawnjelly
Copy link
Member

@lawnjelly lawnjelly commented Jun 19, 2025

Now that these node types are using LocalVector for children, we have the opportunity to speed up child access in loops by using the unguarded version (ptr()[]) and saving needless ERR_FAIL_INDEX checks.

Really inside loops in single threaded code, there is no good reason to use the [] operator in LocalVector. We have already checked the size condition at the start of the loop, so it should never trigger.

ptr()[] / get_unchecked()

Using ptr()[] we have to be aware of a race condition where the number of elements is changed from another thread during the loop. It is NOT thread safe (but neither is the previous).

Node ** children = data.children.ptr()

We can also alternatively grab ptr() before the loop (and I've done that in a couple of hotspots).

There are a few possible gotchas to watch for:

  1. If the child vector changes during the iteration.
    Then the pointer may become invalid (as the vector may be moved in memory).
  2. If the number of children changes during iteration
    Getting the number of children each loop is safer, and in many cases the compiler should optimize out (although this may be worth revisiting at a later stage to make sure in a register)
  3. Multithread access
    Again if this happens, all bets are off. There is already no thread protection for these functions in 3.x, so no change in assumptions here.

Notes

  • I've also removed children_lock from Spatial as it is unused.
  • I originally accessed the children using ptr()[] form, however as that can be difficult to read I added get_unchecked() syntactic sugar.
  • get_unchecked() offers the opportunity in future to do DEV_ENABLED checks only.

Copy link
Member

@Ivorforce Ivorforce left a comment

Choose a reason for hiding this comment

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

For me this is more of an indicator to use iterators instead.

Iterators use ptr iteration (or should at least, not sure where 3.x is at), and so are optimal on that front. But we don't need to do the manual 'ptr()' dance to avoid redundant index checks.

@lawnjelly
Copy link
Member Author

lawnjelly commented Jun 22, 2025

For me this is more of an indicator to use iterators instead.

As I said before, I hate iterators with a passion, I prefer to see exactly what is happening.

Many of us older gen prefer to use c++ as c with classes, and minimize the overly clever c++ stuff. Juan has written quite a bit on this subject, and keeping the codebase accessible as much as possible has been important in allowing contributors to get into it as easily as possible.

3.x hardly uses any iterators, and I'm keen to keep it that way. If the project as a whole were to insist on iterators, I would move on to a different project.

I did originally add a function get_unchecked() to get around the awkward ptr()[] syntax, maybe that would be better.

@lawnjelly lawnjelly force-pushed the faster_children_iteration branch from a43adea to 53c7ee7 Compare June 22, 2025 16:50
@lawnjelly lawnjelly force-pushed the faster_children_iteration branch from 53c7ee7 to aa3dc85 Compare June 22, 2025 16:52
@lawnjelly lawnjelly requested a review from Calinou July 1, 2025 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants