Reputation: 21
Preface: I'm an economist--no formal training in programming.
I'm putting together an ABM in Netlogo and, as the code has gotten longer, the "check" function--required to run the code after any changes--is taking longer and longer. It now takes about 30 seconds on my very fast desktop. I tried running the code on a very slow ultrabook and it took 10 minutes to check. I'd like to know if there's any way to speed up or bypass this process, or if there are any best practices to structure my code in such a way that it can be processed faster.
Edit: I have a great deal of code (about 5,000 lines, 100,000 characters) and the "check" time has increased proportionally as the program got larger. I don't know which components of the code cause the execution time increase or how to simplify the model into pseudocode in a way that's pertinent to this problem.
However, I've added sample code that captures one element of my program (many breed types--though not this many) and takes a fair amount of time to check.
My model has many breed types because I have thousands of agents and need to regularly call searches for specific agents, and found agentsets far too slow.
breed [types1 type1]
breed [types2 type2]
...
breed [types250 type250]
types1-own [ a b c d]
types2-own [ a b c d]
...
types250-own [ a b c d]
globals [
iter
]
to setup
ca
ask types1 [set a 1 set b 2 set c 3 set d 4 set xcor 1 set ycor 1]
ask types2 [set a x set b y set c z set d w set xcor 15 set ycor 12]
....
ask types250 [set a A set b B set c C set d D set xcor 3 set ycor 14]
end
Edit 2:
In response to suggestions that the number of breeds might be a problem, I dropped the number of breeds from about 75 to about 25 and replaced them with agentsets. This significantly improved the setup time in my model (from ~0.8 seconds to ~0.3 seconds), but didn't have a noticeable effect on syntax checking/compiling/whatever is actually happening when "check" is clicked.
Upvotes: 2
Views: 98
Reputation: 2780
I made a sample NetLogo model with 250 breeds and 250 breeds-own
declarations, and it checked/compiled pretty quickly on my 2015 MacBook Pro, less than 1 second. I added some setup
commands for each breed like create-types1 [ ; ... ]
and checking got a little slower. Then I added some go
statements as in your example like, ask types1 [ ; ... ]
and it got slower still, getting to about 5 seconds for about 1000 lines of code.
I opened the DNA Replication Fork model from the models library, which is about 900 lines of code and has about a dozen breeds, and it also seemed to take about 5 seconds to load. This is very unscientific, I was just trying to get a handle on the source of the problem. At least on my MacBook, I can't quite confirm that the many breeds are the culprit for a slow check/compile, but it would certainly be an unusual way to write a model so I wouldn't count it out, either.
Either way, 30 seconds to 10 minutes definitely sounds like too long for a check/compile. If you're willing to submit your full model to [email protected] we can take a closer look and possibly open up an issue for whatever the core cause of the slowness is to eventually get a fix for.
One thing I ran into in creating the below example: NetLogo desktop was very slow to update the view when I called reset-ticks
with ~75,000 agents. You mention running with thousands of agents, but I wasn't sure if it was quite that many. In any case, if you don't need to see some agents while your model runs, you can do a set hidden? true
when you create them, or turn off view updates entirely.
You mention that switching to the many-breeds helped your model's runtime performance, so I don't think this was your issue, but I want to mention it just in case.
At the risk of answering a question you did not ask, here is an option to try to get the unbreeded solution working.
As JenB pointed out, you can pre-store agentsets in variables to avoid repeatedly computing them. The example you give just sets the breeds-own variable values for each type, which I would not expect to be very slow just using filtered agentsets (but I could be wrong!).
You mention looking for specific agents, and I'm wondering if there was a nested-search problem here. Something like ask turtles with [type = 1] [ ask turtles with [type = 2] [ ; ... do something ] ]
could get very slow with lots of agents because that inner turtles with [type = 2]
will get calculated for each type 1 turtle. Pre-calculating the filtered agentsets should help with that, either once per setup
if you agents don't die or change type, or once per go
tick if they do.
Here is an example. I'm using the table
extension to store the pre-calculated agentsets with this version, because with so many types of agents it'll simplify the code quite a bit. In fact, it already has a primitive group-agents
that handles grouping agents by the values of their variables. Because I don't know exactly what the agents are doing in your model, I've filled in some go
code that sets some links just to show how you actually use the stored agent sets.
extensions [ table ]
globals [ turtle-type-numbers turtle-sets ]
turtles-own [ turtle-type a b c d ]
to setup
clear-all
; store the turtle type number list so we don't keep re-creating it
set turtle-type-numbers range 250
set turtle-sets table:make
foreach turtle-type-numbers [ turtle-type-number ->
create-turtles 100 [
set turtle-type turtle-type-number
set hidden? true
set a random 10
set b random 10
set c one-of [true false]
set d one-of ["apples" "oranges" "bananas"]
]
]
reset-ticks
end
to go
; at the start of each tick, re-query our turtle-sets
; if new turtles are not made and existing turtles do not change types
; then this could just be done once at the end of `setup` instead
set turtle-sets table:group-agents turtles [turtle-type]
ask links [ die ]
foreach turtle-type-numbers [ turtle-type-number ->
let typed-turtles (table:get turtle-sets turtle-type-number)
ask typed-turtles [
fd turtle-type mod 10
; link this turtle to one of the other types
; we just choose the next type number, get the agentset
; we stored previosuly, then choose `one-of` those
; turtles to link to.
let next-type (turtle-type + 1) mod 250
let next-turtles (table:get turtle-sets next-type)
create-link-with one-of next-turtles
]
]
tick
end
I hope that is helpful.
Upvotes: 3