Skip to content

Optimise Wolf-sheep performance#3503

Merged
quaquel merged 3 commits intomesa:mainfrom
PietroMondini:opt1
Mar 12, 2026
Merged

Optimise Wolf-sheep performance#3503
quaquel merged 3 commits intomesa:mainfrom
PietroMondini:opt1

Conversation

@PietroMondini
Copy link
Copy Markdown
Contributor

Approval Link

#2565

Summary

Optimizes the movement logic in the wolf-sheep example by replacing redundant CellCollection.select() passes with a single manual loop using an early exit strategy.

Motive

The previous implementation performed two separate .select() passes over the neighborhood on every agent step — one to filter wolf-free cells, and one to find grass. Each pass iterated over all agents in all neighboring cells. In dense simulations with many agents, this becomes a significant bottleneck.

Implementation

Replaced the two CellCollection.select() calls in Sheep.move() with a single manual loop over the neighborhood.
Added has_wolf / has_grass flags in order to break immediately upon finding a wolf, skipping remaining agents in that cell, and check both wolf and grass in the same loop.

@PietroMondini PietroMondini marked this pull request as ready for review March 10, 2026 17:10
@github-actions
Copy link
Copy Markdown

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
BoltzmannWealth small 🔴 +5.2% [+4.2%, +6.1%] 🔵 -2.1% [-2.3%, -1.9%]
BoltzmannWealth large 🔵 +3.1% [+2.1%, +4.1%] 🔴 +14.9% [+12.0%, +17.3%]
Schelling small 🔴 +6.0% [+5.3%, +6.8%] 🔵 +1.5% [+0.8%, +2.2%]
Schelling large 🔵 +2.6% [+1.8%, +3.4%] 🔴 +14.4% [+12.7%, +16.3%]
WolfSheep small 🔴 +8.8% [+8.3%, +9.2%] 🟢 -15.4% [-16.4%, -14.2%]
WolfSheep large 🔵 +2.8% [-0.0%, +5.6%] 🟢 -16.5% [-19.5%, -12.8%]
SugarscapeG1mt small 🔵 -4.6% [-6.3%, -2.8%] 🟢 -5.5% [-6.5%, -4.5%]
SugarscapeG1mt large 🔵 -1.6% [-4.9%, +1.8%] 🔵 -3.6% [-5.3%, -2.2%]
BoidFlockers small 🟢 -4.3% [-5.2%, -3.4%] 🟢 -3.7% [-4.1%, -3.5%]
BoidFlockers large 🟢 -4.8% [-5.5%, -4.2%] 🔵 -2.6% [-3.0%, -2.3%]

@github-actions
Copy link
Copy Markdown

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
BoltzmannWealth small 🔵 -1.4% [-2.0%, -0.8%] 🔵 -0.3% [-0.4%, -0.2%]
BoltzmannWealth large 🔵 -2.6% [-3.2%, -1.8%] 🔵 -0.7% [-1.7%, +0.6%]
Schelling small 🔵 -2.7% [-3.1%, -2.4%] 🔵 -0.9% [-1.0%, -0.7%]
Schelling large 🔵 -1.2% [-1.7%, -0.7%] 🔵 -0.7% [-1.9%, +0.6%]
WolfSheep small 🔵 -1.4% [-1.9%, -1.0%] 🟢 -19.6% [-20.7%, -18.6%]
WolfSheep large 🔵 +0.1% [-0.7%, +0.7%] 🟢 -24.9% [-26.0%, -23.7%]
SugarscapeG1mt small 🔵 -0.8% [-1.2%, -0.4%] 🔵 -0.6% [-0.8%, -0.5%]
SugarscapeG1mt large 🔵 +0.2% [-1.2%, +1.4%] 🔵 +0.0% [-0.4%, +0.4%]
BoidFlockers small 🔵 +1.4% [+0.8%, +2.1%] 🔵 -2.2% [-2.3%, -2.1%]
BoidFlockers large 🔵 +0.6% [-0.1%, +1.4%] 🔵 -1.6% [-1.8%, -1.4%]

# Among safe cells, pick those with grown grass
if has_grass:
cells_with_grass.append(cell)

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.

I have been looking at this code myself before as well and agree with your improvement. I am in principle fine with merging this. However, I am wondering whether a further improvement might not be possible: what about implementing grass as a property layer rather than as agent?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the feedback! Honestly it also feels more conceptually correct to me, since grass is more a passive property of a cell rather than an active agent. Moving it to a PropertyLayer would eliminate the need to iterate over cell.agents entirely when checking for grass.
I've been looking into implementing this and i have one question: should the grass regrowth still be handled via model.schedule_event(), or it'll be better with a different approach?

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.

I would still ike to use scheduling, but if we use a property layer, there is no natural place for the scheduling. It would have to be done inside the sheep when its eating a patch. From an encapsulation point of view, this seems a bit strange.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A potential solution could be to keep the scheduling part inside the GrassPatch agent and move the state to the model into a fully_grown PropertyLayer.
So GrassPatch becomes a pure behavior agent with no state of his own. That's what i'm currently implementing.

Would it make sense to merge the current early exit optimization as-is and open a separate issue to track this work?

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.

that's fine with me.

@quaquel quaquel added the performance Release notes label label Mar 11, 2026
@quaquel quaquel merged commit 494334c into mesa:main Mar 12, 2026
16 checks passed
@PietroMondini PietroMondini deleted the opt1 branch March 12, 2026 11:38
@EwoutH EwoutH added the example Changes the examples or adds to them. label Mar 13, 2026
EwoutH pushed a commit that referenced this pull request Mar 15, 2026
* Early exit for sheeps

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Pietro Mondini <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
EwoutH pushed a commit that referenced this pull request Mar 15, 2026
* Early exit for sheeps

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Pietro Mondini <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

example Changes the examples or adds to them. performance Release notes label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants