thanks to @Dan great idea of using ThreadSafeDicts.jl and as soon as Enlarge DictABM and VecABM for dispatch by Tortar · Pull Request #993 · JuliaDynamics/Agents.jl · GitHub is merged, ThreadSafeDicts
is usable in the dev
version of Agents.jl (soon v6). I have ported your MWE to v6 (while improving some other perf aspects of it), you can run it with more than 1 thread and it works:
using Agents, Random, Distributed, ThreadSafeDicts, BenchmarkTools
@agent struct Person(NoSpaceAgent)
type::Symbol
height::Int64
end
function random_h!(person, model)
person.height = rand(abmrng(model), 1:100)
end
function die!(person, model)
if person.height < model.height_to_die
remove_agent!(person, model)
end
end
function add_people!(person, model)
if person.type === :young && person.height > model.height_to_generate
for _ in 1:10
add_agent!(model, rand(abmrng(model), (:baby, :young, :adult)), rand(abmrng(model), 1:100))
end
elseif person.type === :adult && person.height > model.height_to_generate
for _ in 1:10
add_agent!(model, rand(abmrng(model), (:baby, :young, :adult)), rand(abmrng(model), 1:100))
end
end
end
function evolve_environment!(model)
sum_h = 0.0
for person in allagents(model)
sum_h += person.height
end
model.sum_h[] = sum_h
end
function complex_step!(model)
all_ids = collect(allids(model))
Threads.@threads for i in all_ids
person = model[i]
random_h!(person, model)
die!(person, model)
end
young_adults_ids = collect(Iterators.filter(id -> model[id].type === :young || model[id].type === :adult, allids(model)))
for i in young_adults_ids
person = model[i]
add_people!(person, model)
end
evolve_environment!(model)
#println("agents now: $(nagents(model))")
end
function create_model()
properties = (height_to_die = 30, height_to_generate = 90, sum_h = Ref(0.0))
model = StandardABM(Person; model_step! = complex_step!, properties,
scheduler = Schedulers.fastest, container = ThreadSafeDict,
rng = Xoshiro(42))
for i in 1:100
add_agent!(model, rand(abmrng(model), (:baby, :young, :adult)), rand(1:100))
end
return model
end
@benchmark run!(create_model(), 10)
you can read a rough version of the docs for the new version at Introduction · Agents.jl
edit:
even if it works, it is actually 2x slower than the single-thread version with 12 threads this is somewhat expected, because it’s not easy to do fast multi-threading with non-vector data structures, maybe it is even true that it is faster in the threaded loop, and then it would mean that it is slower because the other parts are not in multi-threading and the multi-threaded structure is slower (maybe it uses a normal Dict
with 1 thread?), not sure about that.