Skip to content

Conversation

@Hammer2900
Copy link
Contributor

Hi Ben,

First off, thanks for creating Esper! I'm a big fan of its clean API and focus on performance.

I was looking through the open issues and saw the discussion in #115 about get_components performance. It's a great point, and it inspired me to take a deeper look and see if I could implement that optimization while staying 100% within the "pure Python" rule.

This PR is the result of that exploration. It directly addresses the suggestion in that issue and adds a few other performance tweaks.

Here’s a quick rundown of the main improvements:

Smarter Multi-Component Queries: The biggest change is in get_components. It now intelligently finds the component with the fewest entities and iterates over that small set first. This makes queries with "rare" components (like a single Player component among hundreds of Enemy components) dramatically faster.

Lazy Cache Invalidation: Instead of clearing the cache on every single change, the world now just marks it as "dirty." It only gets rebuilt on the next query, which cuts down on a lot of unnecessary work, especially in frames with lots of entity creation/deletion.

Instant Processor Lookups: I switched the processor management from a list search to a dictionary lookup, so get_processor and remove_processor are now O(1) operations. It's a small change, but it feels right for a performance-oriented library.

To back this up, I've expanded the benchmark.py script with a couple of new scenarios that specifically highlight these improvements. The new "Rare Component" benchmark really shows off the new query logic in action.

I've also updated the unit tests to pass and added a few new ones to cover the new mechanics. I did my best to keep the changes clean and true to the spirit of the project.

Would love to get your feedback on this when you have a moment. Happy to discuss any of the changes or make adjustments.

Thanks again for your work on Esper!

This commit introduces several significant performance improvements to the core
ECS functionality, addressing the bottlenecks discussed in issue benmoran56#115.

Key changes include:
- `get_components` now intelligently iterates over the smallest component set,
  dramatically speeding up queries with rare components.
- Implemented lazy cache invalidation to reduce overhead during frames
  with heavy entity creation/deletion.
- Switched processor management to use a dictionary for O(1) lookups.

To validate these improvements, the benchmark script has been expanded with
new scenarios. All existing tests have been updated to pass, and new tests
have been added to cover the new mechanics.

Fixes benmoran56#115
except KeyError:
pass
min_set = None
min_size = float('inf')
Copy link
Owner

Choose a reason for hiding this comment

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

How about from math import inf as _inf, and using that here? Might that be faster than creating the float object each time? I think it might be, because we can skip the internal parsing 🤔

Copy link
Contributor Author

@Hammer2900 Hammer2900 Oct 23, 2025

Choose a reason for hiding this comment

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

Interesting case, good point. Tests became a little faster.

@benmoran56
Copy link
Owner

Thank you for the kind words; I'm always happy to see others making use of esper.

This PR is very extensive. Thanks for taking the time to update the unit tests, and adding release notes. The public API doesn't change, so I will push out a new release right away.

mypy is still complaining about the unit tests, but I have been meaning to re-visit the type hints anyway. For example, I am planning to make use of from __future__ import annotations etc. to make use of the modern typing syntax.

Feel free to fix the errors or add an ignore for now, but don't need to spend too much time on it.

@benmoran56 benmoran56 merged commit 11dfc7e into benmoran56:master Oct 27, 2025
36 checks passed
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