Go ECS Benchmarks

June 1, 2026 · View on GitHub

Comparative benchmarks for Go Entity Component System (ECS) implementations.

Disclaimer: This repository is maintained by the author of Ark.

Benchmark candidates

ECSTestedLatestActivity
Arkv0.8.3GitHub Tag GitHub Release DateLast commit
Donburiv1.15.7GitHub Tag GitHub Release DateLast commit
go‑gameengine‑ecsv0.9.0GitHub Tag GitHub Release DateLast commit
GOKev1.2.6GitHub Tag GitHub Release DateLast commit
unitoftime/ecsv0.0.3GitHub Tag GitHub Release DateLast commit
Voltv1.7.0GitHub Tag GitHub Release DateLast commit

Candidates are always displayed in alphabetical order.

In case you develop or use a Go ECS that is not in the list and that want to see here, please open an issue or make a pull request. See the section on Contributing for details.

In case you are a developer or user of an implementation included here, feel free to check the benchmarked code for any possible improvements. Open an issue if you want a version update.

Features

ECSType-safe APIID-based APIRelationsEvents[1]Batches[2]Command buffer
Ark
Donburi
go‑gameengine‑ecs
GOKe
unitoftime/ecs
Volt

[1] ECS lifecycle events, allowing to react to entity creation, component addition, ...
[2] Faster batch operations for entity creation etc.

Benchmarks

Last run: Mon, 01 Jun 2026 11:47:54 UTC
CPU: Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz

For each benchmark, the left plot panel and the table show the time spent per entity, while the right panel shows the total time.

Note that the Y axis has logarithmic scale in all plots. So doubled bar or line height is not doubled time!

All components used in the benchmarks have two float64 fields. The initial capacity of the world is set to 1024 where this is supported.

Query

N entities with components Position and Velocity. 10x N entities with components Position.

  • Query all [Position, Velocity] entities, and add the velocity vector to the position vector.

query2comp

NArkArk (tables)DonburiggecsGOKeuotVolt
164.57ns70.10ns63.39ns48.17ns7.34ns18.27ns79.12ns
417.87ns18.49ns29.63ns15.55ns2.82ns6.88ns20.57ns
165.74ns5.35ns22.17ns7.40ns1.47ns4.58ns6.10ns
642.67ns2.00ns19.29ns5.72ns0.84ns3.46ns2.48ns
2561.95ns1.05ns19.92ns5.19ns0.70ns3.22ns1.60ns
1k1.82ns0.90ns19.92ns5.06ns0.68ns3.27ns1.37ns
16k1.78ns0.84ns21.12ns5.05ns0.81ns3.26ns1.28ns
256k1.77ns0.83ns22.89ns5.09ns0.80ns3.20ns1.26ns
1M1.84ns0.96ns30.63ns5.05ns1.07ns3.20ns1.35ns

Note: Donburi, GOKe, unitoftime/ecs, and Volt use a callback-based approach for their query loops. As a result, iteration speed may degrade if the callback contains complex logic and the Go compiler is unable to inline it.

Query fragmented, inner

Query where the matching entities are fragmented over 32 archetypes.

N entities with components Position and Velocity. Each of these N entities has some combination of components C1, C2, ..., C5, so entities are fragmented over up to 32 archetypes.

  • Query all [Position, Velocity] entities, and add the velocity vector to the position vector.

query32arch

NArkArk (tables)DonburiGOKeggecsuotVolt
165.25ns71.57ns62.71ns7.40ns45.74ns17.19ns83.12ns
432.42ns39.13ns32.91ns5.11ns19.04ns14.89ns67.25ns
1623.18ns31.76ns26.46ns4.51ns12.45ns24.54ns62.06ns
6413.08ns16.25ns23.17ns3.43ns8.51ns14.55ns32.74ns
2564.92ns4.80ns21.20ns2.68ns5.76ns6.10ns9.37ns
1k2.67ns1.87ns22.71ns1.26ns5.21ns3.99ns3.47ns
16k1.95ns0.99ns29.90ns0.90ns5.08ns3.29ns1.54ns
256k1.78ns0.83ns67.57ns0.82ns5.05ns3.18ns1.28ns
1M1.84ns1.08ns95.13ns1.23ns5.06ns3.20ns1.37ns

Note: Donburi, GOKe, unitoftime/ecs, and Volt use a callback-based approach for their query loops. As a result, iteration speed may degrade if the callback contains complex logic and the Go compiler is unable to inline it.

Query fragmented, outer

Query where there are 256 non-matching archetypes.

N entities with components Position and Velocity. Another 4 * N entities with Position and some combination of 8 components C1, ..., C8, so these entities are fragmented over up to 256 archetypes.

  • Query all [Position, Velocity] entities, and add the velocity vector to the position vector.

query256arch

NArkArk (tables)DonburiggecsGOKeuotVolt
162.22ns68.11ns63.51ns55.53ns21.48ns17.18ns100.59ns
417.44ns17.50ns29.59ns25.96ns18.81ns9.72ns47.94ns
165.69ns4.96ns21.19ns19.01ns25.46ns4.73ns36.19ns
642.73ns1.93ns19.45ns16.96ns29.94ns3.79ns37.37ns
2562.01ns1.07ns20.15ns8.13ns12.01ns3.38ns10.51ns
1k1.91ns0.89ns20.08ns5.80ns9.16ns3.17ns3.57ns
16k1.93ns0.82ns20.82ns5.11ns3.91ns3.18ns1.45ns
256k1.96ns0.80ns23.61ns5.06ns6.24ns3.17ns1.28ns
1M2.03ns1.17ns27.19ns5.10ns6.93ns3.20ns1.41ns

Note: Donburi, GOKe, unitoftime/ecs, and Volt use a callback-based approach for their query loops. As a result, iteration speed may degrade if the callback contains complex logic and the Go compiler is unable to inline it.

Component random access

N entities with component Position. All entities are collected into a slice, and the slice is shuffled.

  • Iterate the shuffled entities.
  • For each entity, get its Position and sum up their X fields.

random

NArkDonburiGOKeggecsuotVolt
13.03ns9.42ns16.55ns9.32ns39.12ns13.73ns
42.87ns8.99ns9.12ns9.25ns38.88ns13.49ns
162.96ns8.57ns6.22ns14.28ns39.58ns12.89ns
643.05ns8.65ns5.73ns14.42ns42.86ns12.99ns
2563.18ns9.83ns6.22ns16.21ns43.74ns13.26ns
1k2.99ns15.08ns6.03ns19.51ns43.06ns13.52ns
16k6.20ns45.13ns12.88ns36.85ns68.22ns17.86ns
256k9.15ns165.38ns19.22ns118.57ns164.13ns25.43ns
1M48.20ns277.23ns85.79ns180.91ns253.13ns118.56ns

Create entities

  • Create N entities with components Position and Velocity.

The operation is performed once before benchmarking, to exclude memory allocation, archetype creation etc. See the benchmark below for entity creation with allocation.

create2comp

NArkArk (batch)DonburiggecsGOKeuotVolt
1235.14ns263.06ns1.25us442.41ns254.34ns488.68ns726.32ns
486.04ns73.56ns503.18ns175.09ns100.07ns207.12ns268.66ns
1646.61ns25.41ns328.37ns142.33ns67.66ns132.84ns148.35ns
6435.31ns12.73ns274.88ns133.48ns60.88ns107.74ns135.57ns
25630.88ns10.09ns205.76ns111.91ns54.11ns99.04ns92.58ns
1k30.39ns10.29ns191.49ns110.87ns49.71ns360.79ns87.01ns
16k23.06ns8.21ns207.83ns117.70ns51.25ns352.05ns84.02ns
256k22.70ns8.25ns216.70ns132.35ns50.38ns412.51ns83.55ns
1M22.83ns8.43ns183.99ns226.05ns50.75ns465.49ns83.62ns

Create entities, allocating

  • Create N entities with components Position and Velocity.

Each round is performed on a fresh world. This reflects the creation of the first entities with a certain components set in your game or application. As soon as things stabilize, the benchmarks for entity creation without allocation (above) apply.

Low N values might be biased by things like archetype creation and memory allocation, which is handled differently by different implementations.

create2comp_alloc

NArkArk (batch)DonburiGOKeggecsuotVolt
18.77us8.83us5.55us694.23ns21.77us3.53us1.52us
42.26us2.32us1.64us225.30ns4.95us1.21us655.55ns
16601.53ns564.16ns656.56ns110.01ns1.68us409.25ns275.95ns
64176.66ns154.87ns435.00ns72.27ns598.60ns227.93ns169.58ns
25671.05ns48.73ns329.15ns62.45ns274.29ns181.97ns151.35ns
1k42.87ns22.29ns311.29ns60.22ns240.93ns166.74ns125.64ns
16k51.97ns43.27ns432.03ns58.67ns257.71ns208.14ns152.06ns
256k63.64ns33.21ns490.25ns61.21ns590.99ns213.69ns146.03ns
1M41.78ns21.55ns438.47ns55.87ns1.13us271.69ns122.60ns

Create large entities

  • Create N entities with 10 components C1, ..., C10.

The operation is performed once before benchmarking, to exclude things like archetype creation and memory allocation.

create10comp

NArkArk (batch)DonburiggecsGOKeuotVolt
1271.74ns231.29ns2.05us552.75ns288.18ns755.48ns2.69us
494.90ns64.02ns1.29us244.37ns134.83ns397.72ns1.52us
1645.79ns22.55ns1.07us213.33ns100.37ns276.22ns1.22us
6435.34ns12.76ns846.41ns221.80ns89.94ns261.02ns967.33ns
25630.04ns10.19ns732.01ns162.32ns78.07ns209.06ns911.42ns
1k30.64ns9.21ns733.47ns165.32ns105.08ns477.30ns906.52ns
16k23.03ns7.91ns744.09ns167.40ns90.14ns485.23ns919.34ns
256k25.03ns8.02ns730.01ns186.51ns77.34ns597.62ns879.88ns
1M23.57ns7.92ns755.32ns254.82ns78.66ns712.05ns884.82ns

Add/remove component

N entities with component Position.

  • Add Velocity to all entities.
  • Remove Velocity from all entities.

One iteration is performed before the benchmarking starts, to exclude memory allocation.

add_remove

NArkArk (batch)DonburiggecsGOKeuotVolt
194.26ns175.26ns424.22ns551.07ns100.93ns346.14ns269.01ns
4103.06ns46.05ns434.91ns517.98ns117.61ns368.82ns288.90ns
16116.21ns22.96ns461.93ns576.86ns115.58ns379.05ns275.31ns
64119.30ns13.88ns478.17ns586.26ns130.25ns390.33ns281.29ns
256113.32ns9.47ns500.93ns634.93ns133.44ns442.01ns346.19ns
1k121.67ns9.69ns484.85ns606.05ns111.59ns776.23ns273.92ns
16k107.45ns10.08ns510.57ns618.32ns124.08ns847.29ns272.76ns
256k120.70ns12.16ns519.11ns919.32ns120.93ns1.31us282.73ns
1M115.79ns12.08ns497.45ns1.08us126.40ns1.46us269.10ns

Add/remove component, large entity

N entities with component Position and 10 further components C1, ..., C10.

  • Add Velocity to all entities.
  • Remove Velocity from all entities.

One iteration is performed before the benchmarking starts, to exclude memory allocation.

add_remove_large

NArkArk (batch)DonburiggecsGOKeuotVolt
1344.97ns459.63ns1.10us969.83ns271.90ns803.15ns1.50us
4401.62ns122.81ns1.14us988.26ns372.31ns823.58ns1.41us
16405.94ns47.65ns1.09us1.05us356.68ns812.59ns1.41us
64412.96ns28.32ns1.09us1.06us356.92ns879.76ns1.41us
256409.67ns22.54ns1.11us1.07us392.21ns994.47ns1.61us
1k431.39ns20.96ns1.24us1.01us425.42ns1.51us1.46us
16k423.38ns26.64ns1.27us1.24us500.33ns1.78us1.60us
256k593.08ns45.17ns1.52us1.63us445.56ns2.16us1.65us
1M495.37ns44.80ns1.58us1.85us523.10ns2.48us1.87us

Delete entities

N entities with components Position and Velocity.

  • Delete all entities

delete2comp

NArkArk (batch)DonburiGOKeggecsuotVolt
1140.62ns243.47ns221.33ns166.36ns340.23ns164.53ns290.58ns
472.64ns66.18ns94.26ns90.07ns172.72ns73.49ns121.19ns
1646.11ns21.11ns72.01ns65.92ns157.56ns54.96ns101.00ns
6437.75ns8.73ns57.01ns59.94ns122.81ns46.59ns76.73ns
25628.45ns7.30ns45.81ns43.72ns110.68ns40.08ns53.74ns
1k28.61ns5.66ns38.58ns29.63ns93.75ns32.88ns55.87ns
16k22.62ns4.04ns42.36ns30.37ns101.84ns37.76ns54.01ns
256k23.27ns4.98ns42.23ns31.82ns241.90ns80.78ns51.83ns
1M25.69ns6.24ns55.25ns33.51ns323.32ns129.66ns56.58ns

Delete large entities

N entities with 10 components C1, ..., C10.

  • Delete all entities

delete10comp

NArkArk (batch)DonburiggecsGOKeuotVolt
1197.78ns303.94ns402.98ns483.48ns232.17ns154.54ns540.20ns
4136.65ns80.35ns200.80ns297.14ns162.98ns72.85ns455.88ns
16147.44ns28.79ns171.13ns260.05ns147.26ns48.67ns322.51ns
64100.46ns14.86ns134.78ns169.22ns93.72ns49.54ns234.96ns
25675.88ns8.60ns107.21ns142.86ns89.24ns42.57ns212.31ns
1k69.26ns7.61ns104.43ns144.76ns85.37ns34.32ns289.04ns
16k66.49ns9.91ns107.97ns166.74ns77.57ns33.49ns206.58ns
256k75.69ns13.74ns150.44ns342.90ns88.27ns78.40ns207.33ns
1M71.36ns16.68ns238.52ns429.94ns92.04ns122.24ns211.13ns

Create world

  • Create a new world
NArkDonburiGOKeggecsuotVolt
130.52us3.34us500.12us307.36us3.24us28.65us

Popularity

Given that all tested projects are on Github, we can use the star history as a proxy here.

Star History Chart

Running the benchmarks

Run the benchmarks using the following command:

go run . -test.benchtime=0.25s

On PowerShell use this instead:
go run . --% -test.benchtime=0.25s

The benchtime limit is required for some of the benchmarks that have a high setup cost which is not timed. They would take forever otherwise. The benchmarks can take up to one hour to complete.

To run a selection of benchmarks, add their names as arguments:

go run . query2comp query32arch

To create the plots, run plot/plot.py. The following packages are required:

  • numpy
  • pandas
  • matplotlib
pip install -r ./plot/requirements.txt
python plot/plot.py

Contributing

Developers of ECS frameworks are welcome to add their implementation to the benchmarks. However, there are a few (quality) criteria that need to be fulfilled for inclusion:

  • All benchmarks must be implemented, which means that the ECS must have the required features
  • The ECS must be working properly and not exhibit serious flaws; it will undergo a basic review by maintainers
  • The ECS must be sufficiently documented so that it can be used without reading the code
  • There must be at least basic unit tests
  • Unit tests must be run in the CI of the repository
  • The ECS must not be tightly coupled to a particular game engine, particularly graphics stuff
  • There must be tagged release versions; only tagged versions will be included here

Developers of included frameworks are encouraged to review the benchmarks, and to fix (or point to) misuse or potential optimizations.