Skip to content

test: add comprehensive ECS benchmarks#21

Open
gauvainw wants to merge 1 commit into
mainfrom
test/ecs-benchmarks
Open

test: add comprehensive ECS benchmarks#21
gauvainw wants to merge 1 commit into
mainfrom
test/ecs-benchmarks

Conversation

@gauvainw

Copy link
Copy Markdown
Owner

What changed and why

Establishes comprehensive benchmarks for all performance-critical ECS paths to enable data-driven optimization via benchstat.

New benchmarks

Benchmark Description
EntityCreation NewEntity + 2x Set (position + velocity)
EntityRecycling Create + Set + Destroy cycle
ArchetypeTransition Add/Remove component on existing entity
Query1_1k Query1.Each over 1K entities
Query2_10k Query2.Each over 10K entities
Query2_100k Query2.Each over 100K entities
Query3_10k Query3.Each over 10K entities
Query4_10k Query4.Each over 10K entities
Query2_Iter_10k Query2.Iter (Go 1.23 iterator) over 10K
EventBus_SyncDispatch Synchronous handler dispatch latency
EventBus_AsyncDispatch Async channel dispatch with background drain
GetComponentValue Reflection-based component read
SetComponentValue Reflection-based component write
WorldUpdate_10k Full Update loop with 10K entities
WorldUpdate_100k Full Update loop with 100K entities

Baseline results (i5-9300H, linux/amd64)

BenchmarkEntityCreation-8             ~7K ns/op    ~500 B/op    9 allocs/op
BenchmarkEntityRecycling-8            ~3K ns/op     56 B/op     3 allocs/op
BenchmarkArchetypeTransition-8        ~500 ns/op     8 B/op     0 allocs/op
BenchmarkQuery1_1k-8                  ~120 us/op     8 B/op     1 allocs/op
BenchmarkQuery2_10k-8                 ~2 ms/op       8 B/op     1 allocs/op
BenchmarkQuery2_100k-8                ~25 ms/op      8 B/op     1 allocs/op
BenchmarkQuery3_10k-8                 ~3 ms/op       8 B/op     1 allocs/op
BenchmarkQuery4_10k-8                 ~2 ms/op       8 B/op     1 allocs/op
BenchmarkQuery2_Iter_10k-8            ~1 ms/op       8 B/op     1 allocs/op
BenchmarkEventBus_SyncDispatch-8      ~70 ns/op      0 B/op     0 allocs/op
BenchmarkEventBus_AsyncDispatch-8     ~240 ns/op     0 B/op     0 allocs/op
BenchmarkGetComponentValue-8          ~340 ns/op    16 B/op     1 allocs/op
BenchmarkSetComponentValue-8          ~220 ns/op     0 B/op     0 allocs/op
BenchmarkWorldUpdate_10k-8            ~660 us/op     8 B/op     1 allocs/op
BenchmarkWorldUpdate_100k-8           ~13 ms/op      8 B/op     1 allocs/op

How to test

go test -bench=. -benchmem ./internal/ecs/

Related issue

Closes #12

Add benchmarks for all performance-critical paths:

Entity: creation with components, recycling (create/destroy cycles),
archetype transitions (add/remove component).

Query: Query1 (1K), Query2 (10K, 100K), Query3 (10K), Query4 (10K),
Query2.Iter (10K). All measure iteration throughput via Each/Iter.

Event bus: synchronous handler dispatch, asynchronous channel dispatch.

Reflection: GetComponentValue and SetComponentValue reflection cost.

World: Update loop at 10K and 100K entity scales.

Component test type: add testDamage for Query4 benchmarks.

Baseline results documented in PR description.

Closes #12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a comprehensive benchmark suite for the ECS package to enable consistent performance tracking (e.g., via benchstat) across entity lifecycle, archetype transitions, queries, event dispatch, reflection-based component access, and full-world updates.

Changes:

  • Added benchmarks covering entity creation/recycling, archetype transitions, query iteration (including iterator-based), event bus dispatch, reflection component get/set, and world update at multiple scales.
  • Introduced a new test-only component type (testDamage) to support Query4 benchmarks.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
internal/ecs/component_test.go Adds testDamage component type used by new benchmarks.
internal/ecs/benchmark_test.go Adds a broad ECS benchmark suite for critical performance paths and larger-scale workloads.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +43 to +51

b.ResetTimer()
for i := range b.N {
e := entities[i%len(entities)]
if i%2 == 0 {
Set(w, e, testVelocity{DX: 1})
} else {
Remove[testVelocity](w, e)
}

Copilot AI Mar 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BenchmarkArchetypeTransition alternates Set/Remove based on the loop index, but the chosen entity also changes each iteration. That means the Remove branch usually targets entities that never had testVelocity, so it mostly exercises the early-return path (no archetype move) rather than a real transition. Consider toggling per-entity state (e.g., track whether each entity currently has testVelocity, or alternate Set/Remove on the same entity) so both add/remove paths actually move between archetypes.

Suggested change
b.ResetTimer()
for i := range b.N {
e := entities[i%len(entities)]
if i%2 == 0 {
Set(w, e, testVelocity{DX: 1})
} else {
Remove[testVelocity](w, e)
}
hasVelocity := make([]bool, len(entities))
b.ResetTimer()
for i := range b.N {
idx := i % len(entities)
e := entities[idx]
if hasVelocity[idx] {
Remove[testVelocity](w, e)
} else {
Set(w, e, testVelocity{DX: 1})
}
hasVelocity[idx] = !hasVelocity[idx]

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test: ECS benchmarks and profiling baselines

2 participants