My agents.jl users are loosely coupled. It should be possible to run them mostly in parallel. The only congestion point is the model variable, where their computational results are summed up.
So I introduced @threads to the for loop over all the agents and got this stacktrace:
ERROR: TaskFailedException
Stacktrace:
[1] wait
@ ./task.jl:345 [inlined]
[2] threading_run(fun::var"#13#threadsfor_fun#8"{var"#13#threadsfor_fun#7#9"{StandardABM{Nothing, User, typeof(Agents.Schedulers.fastest), Dict{Symbol, Int64}, Random.TaskLocalRNG}, Base.ValueIterator{Dict{Int64, User}}}}, static::Bool)
@ Base.Threads ./threadingconstructs.jl:38
[3] macro expansion
@ ./threadingconstructs.jl:89 [inlined]
[4] model_step!
@ ~/src/masiri/Initial model/demo_agents_thread.jl:49 [inlined]
[5] step!(model::StandardABM{Nothing, User, typeof(Agents.Schedulers.fastest), Dict{Symbol, Int64}, Random.TaskLocalRNG}, agent_step!::Function, model_step!::typeof(model_step!), n::Int64, agents_first::Bool)
@ Agents ~/.julia/packages/Agents/kEwy8/src/simulations/step.jl:54
[6] run!(model::StandardABM{Nothing, User, typeof(Agents.Schedulers.fastest), Dict{Symbol, Int64}, Random.TaskLocalRNG}, agent_step!::typeof(dummystep), model_step!::typeof(model_step!), n::Int64; when::Bool, when_model::Bool, mdata::Vector{Symbol}, adata::Nothing, obtainer::Function, agents_first::Bool, showprogress::Bool)
@ Agents ~/.julia/packages/Agents/kEwy8/src/simulations/collect.jl:153
[7] top-level scope
@ ~/src/masiri/Initial model/demo_agents_thread.jl:58
nested task error: MethodError: no method matching firstindex(::Base.ValueIterator{Dict{Int64, User}})
Closest candidates are:
firstindex(::Any, ::Any) at abstractarray.jl:402
firstindex(::Union{Tables.AbstractColumns, Tables.AbstractRow}) at ~/.julia/packages/Tables/AcRIE/src/Tables.jl:182
firstindex(::LinRange) at range.jl:693
...
Stacktrace:
[1] #13#threadsfor_fun#7
@ ./threadingconstructs.jl:69 [inlined]
[2] #13#threadsfor_fun
@ ./threadingconstructs.jl:51 [inlined]
[3] (::Base.Threads.var"#1#2"{var"#13#threadsfor_fun#8"{var"#13#threadsfor_fun#7#9"{StandardABM{Nothing, User, typeof(Agents.Schedulers.fastest), Dict{Symbol, Int64}, Random.TaskLocalRNG}, Base.ValueIterator{Dict{Int64, User}}}}, Int64})()
@ Base.Threads ./threadingconstructs.jl:30
This is my code:
using Agents, Base.Threads
const FIB_NUM = 10
mutable struct User <: AbstractAgent
id::Int
fibonacci_num::Int
end
mutable struct MyModel
agents::Vector{AbstractAgent}
step::Int
total_fib::Float64
end
function fibonacci(n::Int)
if n <= 1
return n
else
return fibonacci(n - 1) + fibonacci(n - 2)
end
end
function user_step!(agent::User, model)
agent.fibonacci_num += fibonacci(FIB_NUM)
model.properties[:total_fib] += agent.fibonacci_num
end
function create_my_model()
n_users = 100
users = [User(i, 0) for i in 1:n_users]
agents = users
properties = Dict(
:total_fib => 0,
:tick => 1
)
model = ABM(User; properties=properties)
for agent in agents
add_agent!(agent, model)
end
return model
end
function model_step!(model)
model.tick += 1
@threads for agent in allagents(model)
user_step!(agent, model)
end
end
model = create_my_model()
n_steps = 356
run!(model, dummystep, model_step!, n_steps-1, mdata = [:total_fib])
println("total Fibonacci:", model.total_fib )
What goes on in this stack trace? … WHY? How can I make this run in parallel?
This must be a common request - how are you supposed to parallelize your agents’ computations?