# \[ANN\] LightSumTypes.jl v4

**URL:** <https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741>\
**Category:** Package Announcements\
**Tags:** package, announcement, performance, macros, type-stability\
**Created:** [July 7, 2024, 11:40pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741 "2024-07-07T23:40:50Z")\
**Posts on this page:** 1\
**Showing post:** 15

<div class="post-metadata">

**Author:** ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)\
**Post date:** [July 10, 2024, 9:46pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/15 "2024-07-10T21:46:00Z")

</div>

It was cool to implement this package and it works quite well on Julia \<=1.10, it really patches Union-splitting there.

However, I’m seeing an incredible performance improvement in Union-splitting in 1.11rc1 for some reason, it seems to almost always work automatically which is really cool, e.g. all the benchmarks related to it here [Union splitting vs C++](https://discourse.julialang.org/t/union-splitting-vs-c/61772) are okay now, no dynamic dispatch anywhere.

By extensive testing I found some generators having dynamic dispatch, but not more than that. There are some insights from this package which can maybe lead to some more improvements, e.g. actually in some cases the storage required is much less than what Julia currently achieves with multiples types, but Union-splitting works quite well now! So if you can update to Julia 1.11, even if I think this approach has some more guarantees, I would just first try to work with a `Union` 🙂

The battle is indeed much harsher there:

 ![display](https://global.discourse-cdn.com/julialang/original/3X/e/4/e44e7730da6dbb1163846725dfc7608d41d915d6.png)

> **Code**
>
> ```julia
> # The following simple model has a variable number of agent types,
> # but there is no removing or creating of additional agents.
> # It creates a model that has the same number of agents and does
> # overall the same number of operations, but these operations
> # are split in a varying number of agents. It shows how much of a
> # performance hit is to have many different agent types.
> 
> using Agents, DynamicSumTypes, Random, BenchmarkTools
> 
> tn = 100
> agent_types_s = [Symbol(:Agent, i) for i in 1:tn]
> 
> for (i, t) in enumerate(agent_types_s)
> eval(:(@agent struct $t(GridAgent{2})
> money::Any
> end))
> end
> 
> for i in 1:tn
> eval(:(@sumtype $(Symbol(:AgentAll, i))($(agent_types_s[1:i]...)) <: AbstractAgent))
> end
> 
> const agent_types = Tuple(eval.(agent_types_s))
> const agent_all_t = NamedTuple(Symbol(:AgentAll, i) => eval(Symbol(:AgentAll, i)) for i in 1:tn)
> 
> function initialize_model_1(;n_agents=600,dims=(5,5))
> space = GridSpace(dims)
> model = StandardABM(Agent1, space; agent_step!,
> scheduler=Schedulers.Randomly(),
> rng = Xoshiro(42), warn=false)
> id = 0
> for id in 1:n_agents
> add_agent!(Agent1, model, 10)
> end
> return model
> end
> 
> function initialize_model_sum(;n_agents=600, n_types=1, dims=(5,5))
> agents_used = agent_types[1:n_types]
> agent_all = agent_all_t[Symbol(:AgentAll, n_types)]
> space = GridSpace(dims)
> model = StandardABM(agent_all, space; agent_step!,
> scheduler=Schedulers.Randomly(), warn=false,
> rng = Xoshiro(42))
> agents_per_type = div(n_agents, n_types)
> for A in agents_used
> add_agents!(A, model, agents_per_type, agent_all)
> end
> return model
> end
> 
> function initialize_model_n(;n_agents=600, n_types=1, dims=(5,5))
> agents_used = agent_types[1:n_types]
> space = GridSpace(dims)
> model = StandardABM(Union{agents_used...}, space; agent_step!,
> scheduler=Schedulers.Randomly(), warn=false,
> rng = Xoshiro(42))
> agents_per_type = div(n_agents, n_types)
> for A in agents_used
> add_agents!(A, model, agents_per_type)
> end
> return model
> end
> 
> function add_agents!(A, model, n)
> for _ in 1:n
> a = A(model, random_position(model), 10 + rand(abmrng(model), 1:10))
> add_agent!(a, model)
> end
> return nothing
> end
> function add_agents!(A, model, n, W)
> for _ in 1:n
> a = W(A(model, random_position(model), 10 + rand(abmrng(model), 1:10)))
> add_agent!(a, model)
> end
> return nothing
> end
> 
> function agent_step!(agent, model)
> move!(agent, model)
> agents = agents_in_position(agent.pos, model)
> for a in agents
> exchange!(agent, a)
> end
> return nothing
> end
> 
> function move!(agent, model)
> cell = random_nearby_position(agent, model)
> move_agent!(agent, cell, model)
> return nothing
> end
> 
> function exchange!(agent, other_agent)
> v1 = agent.money
> v2 = other_agent.money
> agent.money = v2
> other_agent.money = v1
> return nothing
> end
> 
> function run_simulation_1(n_steps)
> model = initialize_model_1()
> Agents.step!(model, n_steps)
> end
> 
> function run_simulation_sum(n_steps; n_types)
> model = initialize_model_sum(; n_types=n_types)
> Agents.step!(model, n_steps)
> end
> 
> function run_simulation_n(n_steps; n_types)
> model = initialize_model_n(; n_types=n_types)
> Agents.step!(model, n_steps)
> end
> 
> # %% Run the simulation, do performance estimate, first with 1, then with many
> n_steps = 50
> n_types = [2,3,4,5,10,20,30,40,50,60,70,80,90,100]
> 
> time_1 = @belapsed run_simulation_1($n_steps)
> times_n = Float64[]
> times_multi_s = Float64[]
> for n in n_types
> println(n)
> t = @belapsed run_simulation_n($n_steps; n_types=$n)
> push!(times_n, t/time_1)
> t_sum = @belapsed run_simulation_sum($n_steps; n_types=$n)
> print(t/time_1, " ", t_sum/time_1)
> push!(times_multi_s, t_sum/time_1)
> end
> 
> println("relative time of model with 1 type: 1.0")
> for (n, t1, t2) in zip(n_types, times_n, times_multi_s)
> println("relative time of model with $n types: $t1")
> println("relative time of model with $n @sumtype: $t2")
> end
> 
> using CairoMakie
> fig, ax = CairoMakie.scatterlines(n_types, times_n; label = "Union");
> scatterlines!(ax, n_types, times_multi_s; label = "@sumtype")
> ax.xlabel = "# types"
> ax.ylabel = "time relative to 1 type"
> ax.title = "Union types vs @sumtype"
> axislegend(ax; position = :lt)
> ax.yticks = 0:1:ceil(Int, maximum(times_n))
> ax.xticks = n_types
> fig
> 
> ```

---

_[View the full topic](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741)._
