Skip to content

perf: Skip lifecycle processing work when the queue is empty - #3978

Open
spydon wants to merge 2 commits into
mainfrom
perf/lifecycle-queue-early-out
Open

perf: Skip lifecycle processing work when the queue is empty#3978
spydon wants to merge 2 commits into
mainfrom
perf/lifecycle-queue-early-out

Conversation

@spydon

@spydon spydon commented Aug 5, 2026

Copy link
Copy Markdown
Member

Description

processLifecycleEvents now returns immediately when the queue is empty instead of allocating a set and a closure on every tick, the reorder-parents set is only allocated when a priority change is actually queued, and the blocked-set hash lookups are skipped while the set is empty (the common single-pass case).

Extracted from #3960 so the data-structure change there stands alone (as requested in this comment). Behavior is unchanged; this only removes per-tick allocations and lookups from the game loop.

Checklist

  • I have followed the Contributor Guide when preparing my PR.
  • [-] I have updated/added tests for ALL new/updated/fixed functionality.
  • [-] I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • [-] I have updated/added relevant examples in examples or docs.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Related Issues

Relates to #3957

Comment thread packages/flame/lib/src/components/core/component_tree_root.dart Outdated
final child = event.child!;
final parent = event.parent!;
if (_blocked.contains(child) || _blocked.contains(parent)) {
if (_blocked.isNotEmpty &&

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is the isNotEmpty saving any time here, the contains should be O(0) in that case?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It saves a little bit since it has to calculate the identityHashCode in contains, meanwhile isNotEmpty is virtually free.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this because _blocked is almost always empty? that doesn't appear to necessarily be the case here.

I honestly don't see how a hash check could be any sort of bottleneck, in fact if it were wouldn't the set contains implementation do this? according to clanker the dart identityHashCode implementation is cached per object and saved on the 64-bit pointer header.
I am ok with keeping if we have evidence but I am in favour of having commented out justifications for non-obvious micro-optimizations such as this. as is this looks like a silly mistake. a simple one line comment justifying the cost and why this is such a hot path would make it more clear.

thinking more broadly I am now wondering if this repeat with blocked thing is not ideal to begin with, there might be a way to sort the events in a deterministically correct order, but that is out of scope, just a thought.

Comment thread packages/flame/lib/src/components/core/component_tree_root.dart Outdated

@luanpotter luanpotter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, I am not convinced of the isNotEmpty skip but leave it to you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants