-
Notifications
You must be signed in to change notification settings - Fork 880
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
replace weakref dict with normal dict #1993
Conversation
Performance benchmarks:
|
That are some serious numbers. Quite consistent reduction in runtime of ~25%. Wonder how we can keep the functionality of weakness without the (majority of) the performance impact. At least good to know that this is the effect. |
Cython has |
Didn't we just migrate to hatch? Anyway, that would be great! |
Thanks for checking this. It was also on my list of things to check. I am surprised by the overhead of weakrefs. I knew it is done in pure python and that there thus would be overhead. I never imagined that it was this big. The reason for using weakrefs in AgentSet was to avoid memory leaks. It is also convenient for the user that you don't have to remove agents from the space, the scheduler, and whatever else there might be. However, if the performance overhead is so large, I am wondering whether the cure is not worse than the problem. |
So, I ran into the slowness again. While playing with #1994, I moved |
So the problem seems to be WeakkeyDictionary which is a pure python implementation. It might be possible to substantially improve our speed without losing the use of weakrefs. |
A quick update: I looked into the current implementation of WeakkeyDict. It is basically a clever wrapper around a normal dict with |
Another experiment. I changed the implementation of Agentset.shuffle in 2 ways: I shuffle the weakrefs themselves, and it defaults to inplace updating (quick dirty check, can be changed also within the schedulers. The first column below is the current code on main. I'll do a comparison against @Corvince version with a dict later this morning.
The new code is below. The key slowdown compared to a normal dict is that when iterating over a def shuffle(self, inplace: bool = True) -> AgentSet:
"""
Randomly shuffle the order of agents in the AgentSet.
Args:
inplace (bool, optional): If True, shuffles the agents in the current AgentSet; otherwise, returns a new shuffled AgentSet. Defaults to False.
Returns:
AgentSet: A shuffled AgentSet. Returns the current AgentSet if inplace is True.
"""
weakrefs = list(self._agents.keyrefs())
self.random.shuffle(weakrefs)
if inplace:
self._agents.data = {entry:None for entry in weakrefs}
return self
else:
return AgentSet((agent for ref in weakrefs if (agent:=ref()) is not None), self.model) |
Great work! Curious if we can get towards that 25%, or even more with Cython. |
For completeness: dict (@Corvince version) against updated shuffle. So, there is still a performance loss for using
|
With #2007 merged and my suggested change to shuffle, we are getting close. Here, I compare the dict version to the current main with the updated shuffle implementation. As can be seen, we now lose about 15% with weakrefs. We effectively have halved the performance difference on the large models.
|
Performance benchmarks:
|
After me being stupid for a a bit, I got the benchmarks to run again. So the performance difference of dict compared to weakkeydict is still there but it is a lot smaller than it was before. |
Yes I think the difference is negligible at this point. I think we can close this PR then, do you agree? |
Just noticed that with the cell space testing agentrefs became the main bottleneck. This PR is just to run the benchmark code and see how much difference it makes. Maybe we can talk about the advantages and disadvantages of weakrefs some more. (That is to say I want to start a discussion, not necessarily get this merged).