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

add_agent!(model, properties...) much slower than add_agent!(agent, model) with random position #820

Closed
Tortar opened this issue Jun 18, 2023 · 4 comments
Labels
help wanted Help from someone external is needed performance

Comments

@Tortar
Copy link
Member

Tortar commented Jun 18, 2023

Strangely enough these two ways of adding agents haven't the same performance:

using Agents, BenchmarkTools, Random

@agent B GridAgent{2} begin
    q_1::Int
    q_2::Int
    q_3::Int
    q_4::Int
end

function model_1()
    rng = MersenneTwister(42)
    space = GridSpace((50, 50))
    model = ABM(B, space; rng = rng)
    for n in 1:1000
        agent = B(n, random_position(model), 1, 1, 1, 1)
        add_agent!(agent, model)
    end
    return model
end

function model_2()
    rng = MersenneTwister(42)
    space = GridSpace((50, 50))
    model = ABM(B, space; rng = rng)
    for n in 1:1000
        add_agent!(model, 1, 1, 1, 1)
    end
    return model
end


@benchmark model_1()
@benchmark model_2()

the difference is very big:

julia> @benchmark model_1()
BenchmarkTools.Trial: 10000 samples with 1 evaluation.
 Range (min  max):  126.521 μs    2.872 ms  ┊ GC (min  max):  0.00%  90.20%
 Time  (median):     133.574 μs               ┊ GC (median):     0.00%
 Time  (mean ± σ):   155.014 μs ± 182.597 μs  ┊ GC (mean ± σ):  11.03% ±  8.68%

  ▄██▇▆▅▄▃▃▂▃▂▂▁                                                ▂
  ███████████████▇▇▇▇██▇██▇▆▆▆▆▆▅▅▄▅▅▃▄▁▄▄▁▄▁▃▃▁▁▁▁▃▁▁▁▁▁▁▁▁▁▁▃ █
  127 μs        Histogram: log(frequency) by time        280 μs <

 Memory estimate: 434.19 KiB, allocs estimate: 4385.

julia> @benchmark model_2()
BenchmarkTools.Trial: 5354 samples with 1 evaluation.
 Range (min  max):  794.561 μs    4.562 ms  ┊ GC (min  max): 0.00%  77.38%
 Time  (median):     839.567 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   931.844 μs ± 516.721 μs  ┊ GC (mean ± σ):  8.75% ± 12.23%

  █▇▃                                                           ▁
  ███▇▆▅▄▄▅▃▃▃▁▁▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▇██ █
  795 μs        Histogram: log(frequency) by time       4.09 ms <

 Memory estimate: 1.13 MiB, allocs estimate: 16883.

Still more strangely using instead e.g. add_agent!((1,1), model, 1, 1, 1, 1) has better performance than the first way. I tried to investigate why this happens and didn't find the cause of all of this

@Datseris
Copy link
Member

wow this is tough. From a first glance, the solution is not at all obvious to me...

@Datseris Datseris added the help wanted Help from someone external is needed label Jun 19, 2023
@Datseris
Copy link
Member

So the second method uses the following code and then calls the first method:

function add_agent!(model::ABM{S,A}, properties...; kwargs...) where {S,A<:AbstractAgent}
add_agent!(A, model, properties...; kwargs...)
end
function add_agent!(A::Type{<:AbstractAgent}, model::ABM, properties...; kwargs...)
add_agent!(random_position(model), A, model, properties...; kwargs...)
end
function add_agent!(
pos::ValidPos,
model::ABM{S,A},
properties...;
kwargs...,
) where {S,A<:AbstractAgent}
add_agent!(pos, A, model, properties...; kwargs...)
end
# lowest level:
function add_agent!(
pos::ValidPos,
A::Type{<:AbstractAgent},
model::ABM,
properties...;
kwargs...,
)
id = nextid(model)
newagent = A(id, pos, properties...; kwargs...)
add_agent_pos!(newagent, model)
end

@Tortar
Copy link
Member Author

Tortar commented Jun 22, 2023

Actually now I can reproduce with this "simpler" version the same behaviour in 1.8.3:

using BenchmarkTools

mutable struct A
    q_0::Int
    q_1::Int
    q_2::Int
end

@noinline function g_1()
    for n in 1:1000
        a = A(rand(1:2), 1, 1)
        f(a)
    end
end

@noinline function g_2()
    for n in 1:1000
        f(A, 1, 1)
    end
end

@noinline function f(a::A)
    return a
end

@noinline function f(a::Type{A}, properties...)
    return a(rand(1:2), properties...)
end

@benchmark g_1()
@benchmark g_2()

but I have still no idea why this happens, the only thing I managed to find out is that if I put @inline the difference disappears but I'm not sure why there is a 20x difference otherwise

@Tortar
Copy link
Member Author

Tortar commented Jun 22, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Help from someone external is needed performance
Projects
None yet
Development

No branches or pull requests

2 participants