Skip to content
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

Update multiagent vs. union performance docs for Julia 1.11 #1091

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Agents"
uuid = "46ada45e-f475-11e8-01d0-f70cc89e6671"
authors = ["George Datseris", "Tim DuBois", "Aayush Sabharwal", "Ali Vahdati", "Adriano Meligrana"]
version = "6.1.10"
version = "6.1.11"

[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
Expand Down
14 changes: 5 additions & 9 deletions docs/src/performance_tips.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,7 @@ t = joinpath(dirname(dirname(x)), "test", "performance", "variable_agent_types_s
include(t)
```

We see that Unions of up to three different Agent types do not suffer much.
Hence, if you have less than four agent types in your model, using different types is still a valid option.
For more agent types however we recommend using the [`@multiagent`](@ref) macro.

Finally, we also have a more realistic benchmark of the two approaches at `test/performance/multiagent_vs_union.jl` where the
result of running the model with the two methodologies are
Finally, we also have a more realistic benchmark of the two approaches at [`test/performance/multiagent_vs_union.jl`](https://github.com/JuliaDynamics/Agents.jl/blob/main/test/performance/multiagent_vs_union.jl) where each type has a different set of behaviours, unlike in the previous benchmark. The result of running the model with the two methodologies are

```@example performance_2
using Agents
Expand All @@ -148,7 +143,8 @@ t = joinpath(dirname(dirname(x)), "test", "performance", "multiagent_vs_union.jl
include(t)
```

In reality, we benchmarked the models also in Julia>=1.11 and from that version on a `Union` is considerably
more performant. Though, there is still a general 1.5-2x advantage in many cases in favour of [`@multiagent`](@ref),
so we suggest to use [`@multiagent`](@ref) only when the speed of the multi-agent simulation is really critical.
As you can see, [`@multiagent`](@ref) has the edge over a `Union`: there is a general 1.5-2x advantage in many cases
in its favour. This is true for Julia>=1.11, where we then suggest to go with [`@multiagent`](@ref) only if the speed of the
simulation is critical. However, keep in mind that on Julia<=1.10 the difference is much bigger: [`@multiagent`](@ref)
is almost one order of magnitude faster than a `Union`.

38 changes: 23 additions & 15 deletions test/performance/multiagent_vs_union.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,15 @@ function agent_step!(agent::GridAgentFour, model1)
agent.one += sum(a.one for a in nearby_agents(agent, model1))
end
function agent_step!(agent::GridAgentFive, model1)
targets = filter!(a->a.one > 1.0, collect(types, nearby_agents(agent, model1, 3)))
targets = Iterators.filter(a->a.one > 1.0, nearby_agents(agent, model1, 3))
if !isempty(targets)
idx = argmax(map(t->euclidean_distance(agent, t, model1), targets))
farthest = targets[idx]
walk!(agent, sign.(farthest.pos .- agent.pos), model1)
farthest = 0.0
a = first(targets)
for t in targets
d = euclidean_distance(agent, t, model1)
d > farthest && (a, farthest = t, d)
end
walk!(agent, sign.(a.pos .- agent.pos), model1)
end
end
function agent_step!(agent::GridAgentSix, model1)
Expand All @@ -93,31 +97,35 @@ end

################### DEFINITION 2 ###############

@inline agent_step!(agent, model2) = agent_step!(agent, model2, variant(agent))
agent_step!(agent, model2) = agent_step!(agent, model2, variant(agent))

@inline agent_step!(agent, model2, ::GridAgentOne) = randomwalk!(agent, model2)
@inline function agent_step!(agent, model2, ::GridAgentTwo)
agent_step!(agent, model2, ::GridAgentOne) = randomwalk!(agent, model2)
function agent_step!(agent, model2, ::GridAgentTwo)
agent.one += rand(abmrng(model2))
agent.two = rand(abmrng(model2), Bool)
end
@inline function agent_step!(agent, model2, ::GridAgentThree)
function agent_step!(agent, model2, ::GridAgentThree)
if any(a-> variant(a) isa GridAgentTwo, nearby_agents(agent, model2))
agent.two = true
randomwalk!(agent, model2)
end
end
@inline function agent_step!(agent, model2, ::GridAgentFour)
function agent_step!(agent, model2, ::GridAgentFour)
agent.one += sum(a.one for a in nearby_agents(agent, model2))
end
@inline function agent_step!(agent, model2, ::GridAgentFive)
targets = filter!(a->a.one > 1.0, collect(GridAgentAll, nearby_agents(agent, model2, 3)))
function agent_step!(agent, model2, ::GridAgentFive)
targets = Iterators.filter(a->a.one > 1.0, nearby_agents(agent, model2, 3))
if !isempty(targets)
idx = argmax(map(t->euclidean_distance(agent, t, model2), targets))
farthest = targets[idx]
walk!(agent, sign.(farthest.pos .- agent.pos), model2)
farthest = 0.0
a = first(targets)
for t in targets
d = euclidean_distance(agent, t, model2)
d > farthest && (a, farthest = t, d)
end
walk!(agent, sign.(a.pos .- agent.pos), model2)
end
end
@inline function agent_step!(agent, model2, ::GridAgentSix)
function agent_step!(agent, model2, ::GridAgentSix)
agent.eight += sum(rand(abmrng(model2), (0, 1)) for a in nearby_agents(agent, model2))
end

Expand Down
Loading