Introducing _StrongAgentSet to support strong references in agents.py#3160
Introducing _StrongAgentSet to support strong references in agents.py#3160codebreaker32 wants to merge 26 commits intomesa:mainfrom
Conversation
|
Performance benchmarks:
|
|
Would you mind separating this into 2 PRs? The agent creation one will be straightforward to merge. The agent set stuff is more difficult. I don't want to change the internal weakkeydict to a dict while still under mesa 3.X. It might cause all kinds of unexpected results in existing models that rely on agentset having a weakkeydict. I'm also currently thinking of keeping the existing AgentSet as a weakkeydict, adding a separate dict version used only internally for |
|
I have reverted the creation change.
What if we can keep AgentSet as it is and inherit a new subclass |
Yes that is a logical next step. The main issue however is how to properly integrate this into mesa for However, what happens if someone makes a copy of these hard ref agent sets?. This can happen via any of the methods with an I see two solutions:
|
The implementation I suggested already addresses this naturally. We don't need to write any extra logic to enforce it; the inheritance structure handles it for us. We define class _StrongAgentSet(AgentSet):
"""Internal optimization for Model.agents. Uses strong refs for speed."""
def _storage(self, agents):
return dict.fromkeys(agents)In the base def select(self, ...):
# ...
return AgentSet(agents, self.random) # <--- Explicitly returns a WEAK AgentSet
This automatically creates the safety boundary you described:
For extra safety, we can explicitly override |
|
Thanks for the clarification. This PR currently subclasses the weakkey version from the hard key version. I suggest changing this around. Moreover, there are many optimizations inside the current agent set when we iterate. These really need to stay in place because they have a profound impact on performance. From memory, the one in |
You are completely right, we cannot just swap the storage backend and expect it to work. The current AgentSet implementation is tightly coupled to WeakKeyDictionary specific APIs (like .keyrefs() and .data), which don't exist on a standard dict. To make _StrongAgentSet work, We have to do more than just change _storage; Instead have to override every method that relies on those weakref internals. |
|
I’ve implemented a working version where _StrongAgentSet passes all safety checks and correctly downgrades copies to weak sets. Happy to leave the final merge decision to you |
|
Can you update the title to reflect what this PR does. I'll try to review this tomorrow. A first quick look suggests most if fine. What are your thoughts on updating the internals of Mesa to start using this? |
In my opinion, this change isn't introducing strong ownership; it is optimizing the I think updating
My view: This is a positive standard. Relying on Python's Garbage Collector for simulation logic is non-deterministic. Explicit Integration Plan
I also believe this is be gonna a critical change as AgentSet directly interacts/will interact with AgentDataSet |
EwoutH
left a comment
There was a problem hiding this comment.
Thanks for working on this, interesting performance benefits!
I will leave the full review to Jan, two minor points.
This comment was marked as outdated.
This comment was marked as outdated.
|
For Schelling, you should also update agents_by_type to use the dict based agentset. Since this one is already managed by |
|
Some cons that I could think for this approach are:-
|
|
On the GitHub side, it’s running on shared, virtual runners. Performance is influenced by other workloads. Not the most stable platform for benchmarks (but free).
Small note: We have maintenance branches (or can create ones), which to we can backport specific fixes. |
Agree. But I found some points that maybe we could fix from our side
|
|
and
@codebreaker32, If you have time, a PR would be welcome. Otherwise, I'll take a look hopefully tonight. |
Sure, I am on it |
To give some context, there are good arguments for this: While compute execution is often slower, is rarely faster. The distribution tends to be heavily skewed. When originally developing these benchmarks, using the minimum was a conscious decision with more stable results. That said, improvements to our benchmarking are certainly welcome.
Did we address this yet? |
After testing both approaches(results shared on #3203), I strongly agree with you :)
I thought it is okay for now after what @quaquel said
|
|
Could you check if there's a simple test we could add to check for this? |
I think you are overthinking the issue. First, the API has been very stable. Second, if new stuff is added to agentset, we are likely to remember this and test accordingly. Third, I expect to go in and clean this up in a follow-up PR by adding an ABC AgentSet class that both weakkeydict and normal dict inherit from. |
Also, DW There are enough tests for |
|
Performance benchmarks:
|
|
Above benchmarks ran using old |
|
Performance benchmarks:
|
|
I am reviewing the code locally at the moment. I have a few minor comments still:
|
|
You are right since _StrongAgentSet is a subclass, the typing holds up via inheritance, but it is subtle. Also, I acknowledge that the current structure (with _agent, _all_agents, and the inheritance mix) is due for a cleanup. I see this complete process as a 3-step roadmap to maintain backward compatibility and easy to review:
We can open a tracking issue for this roadmap. If you are fine with this approach, I will proceed |
|
Sounds like a plan. Does this mean that this PR will be superseded and so does not need to be merged, or do you want to start from this PR? |
I am fine either way! If you want to get an ABC in immediately, we can merge this PR by tomorrow( I will finalize it by tonight). Which approach do you prefer? |
|
Let's start from the ABC instead of merging this. The git history will be cleaner and easier to reconstruct in the future, if needed, what changed and why. |
Problem:
AgentSetiteration was unnecessarily expensive due to WeakKeyDictionary. Every iteration required a liveness check for every agent. Sincemodel.agents(the central registry) relies on AgentSet, this overhead impacted every step of the simulation.This PR solves them by:-
_StrongAgentSet, a subclass ofAgentSetthat uses a standard dict for storage instead ofWeakKeyDictionary.AgentSet(weak references), preventing memory leaks in user space.Backward Compatibility: No breaking changes to the public API signature.
Closes #3128