Skip to content

perf: store components in archetype-indexed slices instead of maps#32

Merged
ragoune merged 1 commit into
masterfrom
perf/storage-slice-columns
Jun 28, 2026
Merged

perf: store components in archetype-indexed slices instead of maps#32
ragoune merged 1 commit into
masterfrom
perf/storage-slice-columns

Conversation

@ragoune

@ragoune ragoune commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

ComponentsStorage kept its component columns in a map[archetypeId][]T, hashing on every storage access. archetypeId is dense, so the columns are now held in a [][]T indexed directly by archetypeId, removing the hash from every add, remove, copy and query read.

Query columns are fetched through a bounds-safe getColumn: a query with an optional component matches archetypes that don't contain it, whose id can be beyond that component's column slice — a raw index would panic (a map returned nil). getColumn is kept out of line (//go:noinline) so it does not perturb the hot loops; this also makes iteration slightly faster than the previous map.

The query Foreach loops gain a fast path for the common case where all columns are present, hoisting the per-entity nil checks (needed only for optional components) out of the loop.

@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.48819% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
storage.go 63.15% 7 Missing ⚠️
Files with missing lines Coverage Δ
query.go 86.30% <100.00%> (ø)
storage.go 63.33% <63.15%> (-0.67%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

ComponentsStorage kept its component columns in a map[archetypeId][]T, hashing on every storage access. archetypeId is dense, so the columns are now held in a [][]T indexed directly by archetypeId, removing the hash from every add, remove, copy and query read.

Query columns are fetched through a bounds-safe getColumn: a query with an optional component matches archetypes that don't contain it, whose id can be beyond that component's column slice — a raw index would panic (a map returned nil). getColumn is kept out of line (//go:noinline) so it does not perturb the hot loops; this also makes iteration slightly faster than the previous map.

The query Foreach loops gain a fast path for the common case where all columns are present, hoisting the per-entity nil checks (needed only for optional components) out of the loop.
@ragoune ragoune force-pushed the perf/storage-slice-columns branch from 43f7a94 to 7b56d83 Compare June 28, 2026 12:46
@ragoune ragoune merged commit 0463c96 into master Jun 28, 2026
4 checks passed
@ragoune ragoune deleted the perf/storage-slice-columns branch June 28, 2026 12:47
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.

1 participant