# \[ANN\] Ark.jl v0.3.0: Archetype-based ECS, now with entity relationships and batch operations

**URL:** <https://discourse.julialang.org/t/ann-ark-jl-v0-3-0-archetype-based-ecs-now-with-entity-relationships-and-batch-operations/134791>\
**Category:** Package Announcements\
**Tags:** package\
**Created:** [December 29, 2025, 12:33pm UTC](https://discourse.julialang.org/t/ann-ark-jl-v0-3-0-archetype-based-ecs-now-with-entity-relationships-and-batch-operations/134791 "2025-12-29T12:33:36Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![mlange-42](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mlange-42/32/219037_2.png) [@mlange-42](https://discourse.julialang.org/u/mlange-42)\
**Post date:** [December 29, 2025, 12:33pm UTC](https://discourse.julialang.org/t/ann-ark-jl-v0-3-0-archetype-based-ecs-now-with-entity-relationships-and-batch-operations/134791/1 "2025-12-29T12:33:36Z")

</div>

Ark.jl v0.3 is our biggest feature release yet. It introduces first‑class entity relationships, expands batch operations far beyond entity creation, and delivers substantial performance improvements.

## Why ECS?

_Skip this of you know it already!_

Entity Component Systems (ECS) offer a clean, scalable way to build individual- and agent-based models by separating agent data from behavioral logic. Agents are simply collections of components, while systems define how those components interact, making simulations modular, extensible, and efficient even with millions of heterogeneous individuals.

Ark.jl brings this architecture to Julia with a lightweight, performance-focused implementation that empowers scientific modellers to design complex and performant simulations without the need for deep software engineering expertise.

## Release highlights

### Entity relationships

This release adds first‑class support for entity relationships, allowing you to express connections between entities directly using ECS primitives. While it is possible to express relations by storing entities inside components, the tight integration into the ECS provides several benefits. Most importantly, relationship can be queried now as efficiently as component queries. In addition, relationships become more ergonomic, more consistent, and safer to use.

For details, see the user manual’s chapter on [Entity relationships](https://ark-ecs.github.io/Ark.jl/stable/manual/relations.html).

### Batch operations

Previous versions of Ark.jl already offered blazing‑fast batch entity creation. This release generalizes the concept to all operations that modify entities or their components. You can now remove all entities matching a filter, add components to all matching entities, and more, using a single batched call. These operations are typically at least an order of magnitude faster than performing the same changes individually.

For details, see the user manual’s chapter on [Batch operations](https://ark-ecs.github.io/Ark.jl/stable/manual/batch.html)

### Cached queries

Queries in archetype‑based ECS are already highly efficient, but this release introduces cached queries for even greater performance, especially in worlds with many archetypes. Instead of checking the components of all archetypes in the pre-selection (which is based on the most “rare” component in a query), cached queries maintain a list of all matching archetypes. This means matching checks are only needed when a new archetype is created, eliminating overhead during query iteration.

### Performance improvements

Numerous optimizations to component operations and the archetype graph yield significant speedups. Component operations are now 1.5–2× faster, and entity creation is up to 3× faster than before.

### More

For a full list of all changes, see the [CHANGELOG](https://github.com/ark-ecs/Ark.jl/blob/main/CHANGELOG.md).

As always, your feedback contributions are highly appreciated!

---

<div class="post-metadata">

**Author:** ![slwu89](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/slwu89/32/217323_2.png) [@slwu89](https://discourse.julialang.org/u/slwu89)\
**Post date:** [December 29, 2025, 4:15pm UTC](https://discourse.julialang.org/t/ann-ark-jl-v0-3-0-archetype-based-ecs-now-with-entity-relationships-and-batch-operations/134791/2 "2025-12-29T16:15:09Z")

</div>

Looks very impressive, congratulations on the significant achievements since the initial release. Based on the description of how relationships work in the docs (1:1 between entities), it seems they are functions in the strict mathematical sense. This naturally leads me to think of Ark as being somewhat like an in-memory relational database, with the relations functioning like foreign keys. Is this how you think of Ark/ECS when using the system in designing “real” simulations/projects? Also, I am curious if you have seen [GitHub - AlgebraicJulia/ACSets.jl: ACSets: Algebraic databases as in-memory data structures](https://github.com/AlgebraicJulia/ACSets.jl) before which takes a categorical interpretation to the “sets and functions” approach to in memory relational databases.

---

<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:** [December 30, 2025, 1:39am UTC](https://discourse.julialang.org/t/ann-ark-jl-v0-3-0-archetype-based-ecs-now-with-entity-relationships-and-batch-operations/134791/3 "2025-12-30T01:39:30Z")

</div>

I read some of the discussions on the topics on the internet inspired by your question, and I think that it is (somewhat) of a shared interpretations that ECS can be thought as an in-memory relational database. Though, in respect to other kinds of in-memory databases it is mostly optimized for raw speed and flexibility in operations related to simulations/games. In the end, queries in Ark just iterates over homogeneous arrays so the speed is sort of as good as it can get on these operations.

I find a bit hard to understand what ACSets does since the algebraic viewpoint is a bit unfamiliar to me and at the same time the documentations is very brief. What I can see is that the performance can’t most probably match the one in Ark since its API is not enough “type-based” so it is probably type unstable in some places, but I have to say I don’t really understand how to write an equivalent Ark program with ACSets.

---

<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:** [December 30, 2025, 2:05am UTC](https://discourse.julialang.org/t/ann-ark-jl-v0-3-0-archetype-based-ecs-now-with-entity-relationships-and-batch-operations/134791/4 "2025-12-30T02:05:14Z")

</div>

I now tried to replicate the simple position-velocity ECS benchmark with ACSets.jl, this is what I got:

> **pos-vel with ACSets**
>
> ```julia-auto
> using ACSets
> 
> const SchPhysics = BasicSchema(
> [:Entity],
> [],
> [:Real],
> [
> (:x, :Entity, :Real),
> (:y, :Entity, :Real),
> (:dx, :Entity, :Real),
> (:dy, :Entity, :Real)
> ]
> )
> 
> @acset_type PhysicsWorld(SchPhysics, index=[])
> 
> function run_posvel_acsets()
> world = PhysicsWorld{Float64}()
> 
> n = 10^5
> add_parts!(world, :Entity, n, 
> x = Float64[i for i in 1:n], 
> y = Float64[i * 2 for i in 1:n],
> dx = ones(Float64, n), 
> dy = ones(Float64, n)
> )
> 
> for t in 1:10
> xs = world[:x]
> ys = world[:y]
> dxs = world[:dx]
> dys = world[:dy]
> @inbounds for i in eachindex(xs)
> xs[i] = xs[i] + dxs[i]
> ys[i] = ys[i] + dys[i]
> end
> end
> 
> return world
> end
> 
> ```

which when we benchmark it we get

```julia-auto
julia> @benchmark run_posvel_acsets()
BenchmarkTools.Trial: 22 samples with 1 evaluation per sample.
 Range (min … max): 167.329 ms … 351.249 ms ┊ GC (min … max): 2.46% … 47.08%
 Time (median): 199.468 ms ┊ GC (median): 5.90%
 Time (mean ± σ): 229.912 ms ± 67.248 ms ┊ GC (mean ± σ): 22.50% ± 20.14%

  ▁ ▄ █ ▄          
  █▆▁█▁▁▁▆▆▁█▆▆▁▆▆▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▆▁▁▆▁▆ ▁
  167 ms Histogram: frequency by time 351 ms <

 Memory estimate: 227.00 MiB, allocs estimate: 11970103.

```

instead

> **pos-vel with Ark**
>
> ```julia-auto
> using Ark
> 
> struct Position
> x::Float64
> y::Float64
> end
> 
> struct Velocity
> dx::Float64
> dy::Float64
> end
> 
> function run_posvel_ark()
> world = World(Position, Velocity)
> 
> new_entities!(world, 10^5, (Position, Velocity)) do (entities, positions, velocities)
> for i in eachindex(entities)
> positions[i] = Position(i, i * 2)
> velocities[i] = Velocity(1, 1)
> end
> end
> 
> for i in 1:10
> for (entities, positions, velocities) in Query(world, (Position, Velocity))
> @inbounds for i in eachindex(entities)
> pos = positions[i]
> vel = velocities[i]
> positions[i] = Position(pos.x + vel.dx, pos.y + vel.dy)
> end
> end
> end
> 
> return world
> end
> 
> ```

gives

```julia-auto
julia> @benchmark run_posvel_ark()
BenchmarkTools.Trial: 4582 samples with 1 evaluation per sample.
 Range (min … max): 721.455 μs … 5.888 ms ┊ GC (min … max): 0.00% … 41.52%
 Time (median): 1.014 ms ┊ GC (median): 0.00%
 Time (mean ± σ): 1.086 ms ± 217.248 μs ┊ GC (mean ± σ): 9.77% ± 12.18%

                 ▁▁█▆▂                                           
  ▁▁▁▁▂▂▂▂▂▂▂▂▂▃▅█████▇▅▅▅▅▅▅▅▄▄▃▂▃▂▂▂▂▃▃▃▃▃▃▃▃▄▄▃▃▂▂▂▂▂▂▂▂▂▁▁▁ ▃
  721 μs Histogram: frequency by time 1.55 ms <

 Memory estimate: 7.17 MiB, allocs estimate: 442.

```

so it seems more than 200x slower in this simple benchmark.

In general while trying the library it seems also more restrictive than Ark, e.g. I’m not getting how you could add/remove components to entities at runtime.
