From f6292a9b32448e6cfb279c49b502d878d0939127 Mon Sep 17 00:00:00 2001 From: Ramine Agoune Date: Sun, 28 Jun 2026 12:23:55 +0200 Subject: [PATCH] perf: avoid boxing components into an interface when adding to storage addComponentsToArchetypeN and copy hold a concrete *ComponentsStorage[T], but went through add(component ComponentInterface), boxing the component into an interface and doing a type assertion on every add. Add a typed addTyped(T) that appends the value directly; the interface add now just delegates to it. --- component.go | 72 ++++++++++++++++++++++++++-------------------------- storage.go | 15 ++++++----- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/component.go b/component.go index af0866b..dee04aa 100644 --- a/component.go +++ b/component.go @@ -558,7 +558,7 @@ func addComponentsToArchetype1[A ComponentInterface](world *World, entityRecord world.setArchetype(entityRecord, archetype) } } - storageA.add(archetype.Id, component) + storageA.addTyped(archetype.Id, component) return nil } @@ -583,8 +583,8 @@ func addComponentsToArchetype2[A, B ComponentInterface](world *World, entityReco } } - storageA.add(archetype.Id, componentA) - storageB.add(archetype.Id, componentB) + storageA.addTyped(archetype.Id, componentA) + storageB.addTyped(archetype.Id, componentB) return nil } @@ -610,9 +610,9 @@ func addComponentsToArchetype3[A, B, C ComponentInterface](world *World, entityR } } - storageA.add(archetype.Id, componentA) - storageB.add(archetype.Id, componentB) - storageC.add(archetype.Id, componentC) + storageA.addTyped(archetype.Id, componentA) + storageB.addTyped(archetype.Id, componentB) + storageC.addTyped(archetype.Id, componentC) return nil } @@ -639,10 +639,10 @@ func addComponentsToArchetype4[A, B, C, D ComponentInterface](world *World, enti } } - storageA.add(archetype.Id, componentA) - storageB.add(archetype.Id, componentB) - storageC.add(archetype.Id, componentC) - storageD.add(archetype.Id, componentD) + storageA.addTyped(archetype.Id, componentA) + storageB.addTyped(archetype.Id, componentB) + storageC.addTyped(archetype.Id, componentC) + storageD.addTyped(archetype.Id, componentD) return nil } @@ -670,11 +670,11 @@ func addComponentsToArchetype5[A, B, C, D, E ComponentInterface](world *World, e } } - storageA.add(archetype.Id, componentA) - storageB.add(archetype.Id, componentB) - storageC.add(archetype.Id, componentC) - storageD.add(archetype.Id, componentD) - storageE.add(archetype.Id, componentE) + storageA.addTyped(archetype.Id, componentA) + storageB.addTyped(archetype.Id, componentB) + storageC.addTyped(archetype.Id, componentC) + storageD.addTyped(archetype.Id, componentD) + storageE.addTyped(archetype.Id, componentE) return nil } @@ -703,12 +703,12 @@ func addComponentsToArchetype6[A, B, C, D, E, F ComponentInterface](world *World } } - storageA.add(archetype.Id, componentA) - storageB.add(archetype.Id, componentB) - storageC.add(archetype.Id, componentC) - storageD.add(archetype.Id, componentD) - storageE.add(archetype.Id, componentE) - storageF.add(archetype.Id, componentF) + storageA.addTyped(archetype.Id, componentA) + storageB.addTyped(archetype.Id, componentB) + storageC.addTyped(archetype.Id, componentC) + storageD.addTyped(archetype.Id, componentD) + storageE.addTyped(archetype.Id, componentE) + storageF.addTyped(archetype.Id, componentF) return nil } @@ -738,13 +738,13 @@ func addComponentsToArchetype7[A, B, C, D, E, F, G ComponentInterface](world *Wo } } - storageA.add(archetype.Id, componentA) - storageB.add(archetype.Id, componentB) - storageC.add(archetype.Id, componentC) - storageD.add(archetype.Id, componentD) - storageE.add(archetype.Id, componentE) - storageF.add(archetype.Id, componentF) - storageG.add(archetype.Id, componentG) + storageA.addTyped(archetype.Id, componentA) + storageB.addTyped(archetype.Id, componentB) + storageC.addTyped(archetype.Id, componentC) + storageD.addTyped(archetype.Id, componentD) + storageE.addTyped(archetype.Id, componentE) + storageF.addTyped(archetype.Id, componentF) + storageG.addTyped(archetype.Id, componentG) return nil } @@ -775,14 +775,14 @@ func addComponentsToArchetype8[A, B, C, D, E, F, G, H ComponentInterface](world } } - storageA.add(archetype.Id, componentA) - storageB.add(archetype.Id, componentB) - storageC.add(archetype.Id, componentC) - storageD.add(archetype.Id, componentD) - storageE.add(archetype.Id, componentE) - storageF.add(archetype.Id, componentF) - storageG.add(archetype.Id, componentG) - storageH.add(archetype.Id, componentH) + storageA.addTyped(archetype.Id, componentA) + storageB.addTyped(archetype.Id, componentB) + storageC.addTyped(archetype.Id, componentC) + storageD.addTyped(archetype.Id, componentD) + storageE.addTyped(archetype.Id, componentE) + storageF.addTyped(archetype.Id, componentF) + storageG.addTyped(archetype.Id, componentG) + storageH.addTyped(archetype.Id, componentH) return nil } diff --git a/storage.go b/storage.go index c324427..467d9f8 100644 --- a/storage.go +++ b/storage.go @@ -80,18 +80,21 @@ func (c *ComponentsStorage[T]) size(archetypeId archetypeId) int { } func (c *ComponentsStorage[T]) add(archetypeId archetypeId, component ComponentInterface) int { - // this function could be simplified using: - // c.size(archetypeId) - 1 - // but to reduce the usage of mapaccess we compute the size ourselves instead of calling c.size + return c.addTyped(archetypeId, component.(T)) +} - c.archetypesComponentsEntities[archetypeId] = append(c.archetypesComponentsEntities[archetypeId], component.(T)) +// addTyped appends a component without boxing it into ComponentInterface. +// The generic add/copy paths hold a concrete *ComponentsStorage[T], so they can +// call this directly and avoid one heap allocation per component added. +func (c *ComponentsStorage[T]) addTyped(archetypeId archetypeId, component T) int { + // We compute the size ourselves instead of calling c.size to reduce mapaccess. + c.archetypesComponentsEntities[archetypeId] = append(c.archetypesComponentsEntities[archetypeId], component) return len(c.archetypesComponentsEntities[archetypeId]) - 1 - } func (c *ComponentsStorage[T]) copy(oldArchetypeId archetypeId, archetypeId archetypeId, recordKey int) int { - return c.add(archetypeId, c.archetypesComponentsEntities[oldArchetypeId][recordKey]) + return c.addTyped(archetypeId, c.archetypesComponentsEntities[oldArchetypeId][recordKey]) } func (c *ComponentsStorage[T]) set(archetypeId archetypeId, key int, component ComponentInterface) {