JuliaDynamics / Agents.jl

Agent-based modeling framework in Julia
https://juliadynamics.github.io/Agents.jl/stable/
MIT License
717 stars 115 forks source link

Use Vectors instead of a Dict for nearby offsets #895

Closed Tortar closed 10 months ago

Tortar commented 10 months ago

This should make the retrieval of the offsets faster.....

The PR removes the dictionaries used to hold the offsets for calculating nearby stuff and substitute them with vectors. The access to a vector should be faster, and so this should help performance

Still needs benchmarks

codecov-commenter commented 10 months ago

Codecov Report

Merging #895 (6eb35fb) into main (5156fa9) will increase coverage by 7.99%. The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main     #895      +/-   ##
==========================================
+ Coverage   84.45%   92.45%   +7.99%     
==========================================
  Files          43       32      -11     
  Lines        2850     2333     -517     
==========================================
- Hits         2407     2157     -250     
+ Misses        443      176     -267     
Files Coverage Δ
src/spaces/grid_general.jl 98.48% <100.00%> (+0.15%) :arrow_up:
src/spaces/grid_multi.jl 83.33% <ø> (ø)
src/spaces/grid_single.jl 100.00% <ø> (ø)
src/submodules/io/jld2_integration.jl 100.00% <100.00%> (ø)

... and 11 files with indirect coverage changes

:mega: We’re building smart automated test selection to slash your CI/CD build times. Learn more

Datseris commented 10 months ago

Hi, this is a complex change... You will need to summarize your PR in english words for me to review it. Deducing what the PR does by looking at the code would take too long for me! Sorry I am just in a really stressful life situation right now, really short on time!!!

Tortar commented 10 months ago

No problem, I will not bother you with this, there are more important changes going on, will try to resolve myself, and if I'm totally lost I will ask you for help

Datseris commented 10 months ago

but you should still start labelling the PRs with an adequate description for future searches and backtracking!

Tortar commented 10 months ago

Done in the main comment 👍

Tortar commented 10 months ago

ok, after the simplification allocations seem to have disappeared (don't understand why but that's it :D), will do a brief macro-benchmark to see how much of an improvement it is

Tortar commented 10 months ago

Indeed, the improvement is pretty substantial, running the Game of Life simulation (which is already very fast, that's why the improvement is substantial)

Benchmark ```julia using Agents, Random, BenchmarkTools @agent Automaton GridAgent{2} begin end function build_model(alive_probability, dims, metric = :chebyshev) space = GridSpaceSingle(dims; metric, periodic=false) status = zeros(Bool, dims) new_status = zeros(Bool, dims) rules = (2, 3, 3, 3) properties = (; rules, status, new_status) model = UnremovableABM(Automaton, space; properties, rng = Xoshiro()) for pos in Agents.positions(model) if rand(model.rng) < alive_probability status[pos...] = true end end return model end function model_step(model) new_status = model.new_status status = model.status @inbounds for pos in Agents.positions(model) n = alive_neighbors(pos, model) if status[pos...] == true && (n ≤ model.rules[4] && n ≥ model.rules[1]) new_status[pos...] = true elseif status[pos...] == false && (n ≥ model.rules[3] && n ≤ model.rules[4]) new_status[pos...] = true else new_status[pos...] = false end end status .= new_status return end function alive_neighbors(pos, model) c = 0 @inbounds for near_pos in nearby_positions(pos, model) if model.status[near_pos...] == true c += 1 end end return c end function run_model_agents() model = build_model(0.5, (100, 100)) Agents.step!(model, dummystep, model_step, 1000) end @benchmark run_model_agents() ```

before:

julia> @benchmark run_model_agents()
BenchmarkTools.Trial: 22 samples with 1 evaluation.
 Range (min … max):  223.142 ms … 280.279 ms  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     227.121 ms               ┊ GC (median):    0.00%
 Time  (mean ± σ):   230.362 ms ±  12.257 ms  ┊ GC (mean ± σ):  0.00% ± 0.00%

   ▁ ▃█
  ▇█▄██▁▄▄▁▄▁▄▁▁▁▁▁▁▁▁▁▁▁▁▁▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▄ ▁
  223 ms           Histogram: frequency by time          280 ms <

 Memory estimate: 1.32 MiB, allocs estimate: 34950.

after:

julia> @benchmark run_model_agents()
BenchmarkTools.Trial: 31 samples with 1 evaluation.
 Range (min … max):  160.820 ms … 172.073 ms  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     162.463 ms               ┊ GC (median):    0.00%
 Time  (mean ± σ):   163.448 ms ±   2.549 ms  ┊ GC (mean ± σ):  0.00% ± 0.00%

  ▁  ▄ ▁ ▁█       ▁  ▁
  █▁▆█▆█▆██▁▁▁▆▆▆▆█▆▆█▆▁▆▁▁▁▁▁▁▁▁▁▁▁▁▁▁▆▁▁▁▁▁▁▁▁▁▁▁▁▁▆▁▁▁▁▁▁▁▁▆ ▁
  161 ms           Histogram: frequency by time          172 ms <

 Memory estimate: 1.32 MiB, allocs estimate: 34944.

it is 30% faster!! So now we are "only" 2x slower than DynamicalGrids.jl (in the single cpu case :D)