Agents.jl
Agents.jl copied to clipboard
Consider StructArrays for internal handling of agent dict
model = ABM(Agent3, GridSpace((100, 100)))
for _ in 1:500
add_agent!(model, rand())
end
aos = collect(values(model.agents)) # 1.770 μs (1 allocation: 4.06 KiB)
soa = StructArray(values(model.agents)) # 3.966 μs (12 allocations: 16.22 KiB)
# ^^ initialisation is slower here, but maybe not with a better collection method
julia> @btime sum(a->a.weight, $aos)
428.955 ns (0 allocations: 0 bytes)
252.1354667521684
julia> @btime sum($soa.weight)
57.521 ns (0 allocations: 0 bytes)
252.13546675216827
It remains to be seen how quick this would make things in general. Looking through most of the codebase, we do tend to use model[id] for id in ids
quite a lot, meaning iteration over an array is certainly something to consider. We don't, mostly, look up model[7]
... So the purpose of the O(1) lookup time in Dict
s may not be as needed. The question is how this change affects the nearest_neighbor and Hood
mechanisms. I'm not a fan of having to do some form of findfirst(isequal(id), soa.id)
, O(n) lookup in those functions...
Building this with an associated Dict{Int,Int}
(id, location in array) has been straightforward, except for one major caveat: no mixed model support. That's about half of the machinery we have here, so in general it may not be viable. Will have to think on it.
Actually, now that this is obviously an issue it essentially means a large portion of user-made agents wont be able to be stored in the model structure. So this is a non-starter. Performance gains are interesting, so I'll consider writing up an Integration example if an idea presents itself.
Ah, I didn't see this issue thread earlier and might have missed it because you opened and closed it on the same day. :D Generally I find the StructArray approach very promising and would like to delve a bit deeper into this if I can find the time at some point. The mixed model support is indeed a problem but there has to be a way around this. A lot more thinking is needed here but it seems too promising to dismiss entirely...
Yep, it could be a boon for certain models just not all. If there's a way to identify which models can work with this strategy and a good way to separate those into a SA model type that doesn't clash with the rest of the API, it'd be a really nice addition. My quick analysis was that would be a hard ask at this point—not impossible though perhaps!
Quick update on this:
I've started an attempt to get this running today and it's indeed not so easy (who would have thought? :D ). In my approach I have dropped the id-dict completely and tried to work solely with a StructArray for model.agents
. Not sure if that's a sensible way to do it but for now it seems to work.
Running the tests currently shows
Test Summary: | Pass Fail Error Total
Agents.jl Tests | 600 19 20 639
and I'm pretty certain that the errors stem from the union types (mixed agent models) which was expected. I'm not going to do anything about them right now and would like to focus on the running but failing tests. They stem from the problem that we access agent properties via model[id].property
which works perfectly fine for getting but not for setting. I've chimed in on an open issue over on the StructArrays.jl repo and hope for some insightful responses over there. Will keep you posted. :)
for multi-agent models: a possibility is to use a tuple of struct arrays . (we will need an additional type that maps ids to tuple indices as well)
I've started an attempt to get this running today
Where is this? can you open a PR so i can see the code @fbanning ? In general having an ID map is mandatory. Although this is not done in any example, for "human tracking" you want to have the id tracked.
I have completely missed this issue lol
hope for some insightful responses over there
Well, the answer was a bit sobering. The struct instances are created on the fly when indexing into a StructArray, so naturally model[id].property
would not work to modify it in place but only model.property[id]
. I didn't know that. Have to think about this and whether there's a way around it. Being able to access agents directly and modify their fields in place is what ABM builds on. To keep our current syntax model[id]
, I have to have a look at possible implementations for setting properties/fields to circumvent this, but I'm not sure it's even possible.
Where is this? can you open a PR so i can see the code
Local branch so far. I just started playing around with StructArrays yesterday and want to try out things first to see whether it even works at all. I hope this is understandable.
In general having an ID map is mandatory.
Why so? Agent IDs are stored in the StructArray itself from which I can then retrieve its index position and by that access the other agent properties. I'm probably missing something here.
Agent IDs are stored in the StructArray itself from which I can then retrieve its index position and by that access the other agent properties.
yeah this is also fine
Published a PR for you to assess the situation and what I've attempted to do.