Repository: mlange-42/arche
Pull request #71 (2023-02-14 12:17:21)

Reduce integer conversions by using more uint32


Patch:
diff --git a/ecs/archetype.go b/ecs/archetype.go
index cf83bfe..ae32fdf 100644
--- a/ecs/archetype.go
+++ b/ecs/archetype.go
@@ -49,24 +49,24 @@ func (a *archetype) Init(capacityIncrement int, components ...componentType) {
 }
 
 // GetEntity returns the entity at the given index
-func (a *archetype) GetEntity(index int) Entity {
-	return *(*Entity)(a.entities.Get(uint32(index)))
+func (a *archetype) GetEntity(index uint32) Entity {
+	return *(*Entity)(a.entities.Get(index))
 }
 
 // Get returns the component with the given ID at the given index
-func (a *archetype) Get(index int, id ID) unsafe.Pointer {
+func (a *archetype) Get(index uint32, id ID) unsafe.Pointer {
 	if !a.Mask.Get(id) {
 		return nil
 	}
-	return a.components[a.indices[id]].Get(uint32(index))
+	return a.components[a.indices[id]].Get(index)
 }
 
 // GetUnsafe returns the component with the given ID at the given index,
 // without checking if the entity contains that component.
 //
 // This is used by queries, where the entity is guaranteed to be in the archetype.
-func (a *archetype) GetUnsafe(index int, id ID) unsafe.Pointer {
-	return a.components[a.indices[id]].Get(uint32(index))
+func (a *archetype) GetUnsafe(index uint32, id ID) unsafe.Pointer {
+	return a.components[a.indices[id]].Get(index)
 }
 
 // Add adds an entity with components to the archetype
@@ -98,11 +98,11 @@ func (a *archetype) AddPointer(entity Entity, components ...componentPointer) ui
 }
 
 // Remove removes an entity from the archetype
-func (a *archetype) Remove(index int) bool {
-	swapped := a.entities.Remove(uint32(index))
+func (a *archetype) Remove(index uint32) bool {
+	swapped := a.entities.Remove(index)
 	len := len(a.components)
 	for i := 0; i < len; i++ {
-		a.components[i].Remove(uint32(index))
+		a.components[i].Remove(index)
 	}
 	return swapped
 }
diff --git a/ecs/query.go b/ecs/query.go
index 10608fd..fd743b6 100644
--- a/ecs/query.go
+++ b/ecs/query.go
@@ -98,13 +98,12 @@ func (q *Query) Next() bool {
 	if q.archetype.Next() {
 		return true
 	}
-	i, a, ok := q.world.nextArchetype(q.filter, q.index)
-	q.index = i
+	index, archetype, ok := q.world.nextArchetype(q.filter, q.index)
+	q.index = index
 	if ok {
-		q.archetype = a
+		q.archetype = archetype
 		return true
 	}
-	q.done = true
 	q.world.closeQuery(&q.queryIter)
 	return false
 }
@@ -113,7 +112,6 @@ type queryIter struct {
 	world     *World
 	archetype archetypeIter
 	index     int
-	done      bool
 	lockBit   uint8
 }
 
@@ -144,20 +142,19 @@ func (q *queryIter) Mask() BitMask {
 // Automatically called when iteration finishes.
 // Needs to be called only if breaking out of the query iteration.
 func (q *queryIter) Close() {
-	q.done = true
 	q.world.closeQuery(q)
 }
 
 type archetypeIter struct {
 	Archetype *archetype
-	Length    int
-	Index     int
+	Length    uint32
+	Index     uint32
 }
 
 func newArchetypeIter(arch *archetype) archetypeIter {
 	return archetypeIter{
 		Archetype: arch,
-		Length:    int(arch.Len()),
+		Length:    arch.Len(),
 	}
 }
 
diff --git a/ecs/storage.go b/ecs/storage.go
index 733860e..97f184f 100644
--- a/ecs/storage.go
+++ b/ecs/storage.go
@@ -146,8 +146,9 @@ func (s *storage) Cap() uint32 {
 // toSlice converts the content of a storage to a slice of structs
 func toSlice[T any](s storage) []T {
 	res := make([]T, s.Len())
-	for i := 0; i < int(s.Len()); i++ {
-		ptr := (*T)(s.Get(uint32(i)))
+	var i uint32
+	for i = 0; i < s.Len(); i++ {
+		ptr := (*T)(s.Get(i))
 		res[i] = *ptr
 	}
 	return res
diff --git a/ecs/world.go b/ecs/world.go
index 648b3d6..eb855dd 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -81,12 +81,12 @@ func (w *World) RemEntity(entity Entity) {
 
 	index := w.entities[entity.id]
 	oldArch := index.arch
-	swapped := oldArch.Remove(int(index.index))
+	swapped := oldArch.Remove(index.index)
 
 	w.entityPool.Recycle(entity)
 
 	if swapped {
-		swapEntity := oldArch.GetEntity(int(index.index))
+		swapEntity := oldArch.GetEntity(index.index)
 		w.entities[swapEntity.id].index = index.index
 	}
 
@@ -126,7 +126,7 @@ func (w *World) Alive(entity Entity) bool {
 // See also [github.com/mlange-42/arche/generic.Map.Get] for a generic variant.
 func (w *World) Get(entity Entity, comp ID) unsafe.Pointer {
 	index := w.entities[entity.id]
-	return index.arch.Get(int(index.index), comp)
+	return index.arch.Get(index.index, comp)
 }
 
 // Has returns whether an [Entity] has a given component.
@@ -263,7 +263,7 @@ func (w *World) Exchange(entity Entity, add []ID, rem []ID) {
 
 	allComps := make([]componentPointer, 0, len(keepIDs)+len(addIDs))
 	for _, id := range keepIDs {
-		comp := oldArch.Get(int(index.index), id)
+		comp := oldArch.Get(index.index, id)
 		allComps = append(allComps, componentPointer{ID: id, Pointer: comp})
 	}
 	for _, id := range addIDs {
@@ -271,10 +271,10 @@ func (w *World) Exchange(entity Entity, add []ID, rem []ID) {
 	}
 
 	newIndex := arch.AddPointer(entity, allComps...)
-	swapped := oldArch.Remove(int(index.index))
+	swapped := oldArch.Remove(index.index)
 
 	if swapped {
-		swapEntity := oldArch.GetEntity(int(index.index))
+		swapEntity := oldArch.GetEntity(index.index)
 		w.entities[swapEntity.id].index = index.index
 	}
 	w.entities[entity.id] = entityIndex{arch: arch, index: newIndex}
@@ -301,7 +301,7 @@ func (w *World) Stats() *stats.WorldStats {
 		arch := w.archetypes.Get(i)
 
 		ids := arch.Components()
-		aCompCount := int(len(ids))
+		aCompCount := len(ids)
 		aTypes := make([]reflect.Type, aCompCount)
 		for j, id := range ids {
 			aTypes[j] = w.registry.ComponentType(id)
@@ -406,7 +406,7 @@ func (w *World) componentID(tp reflect.Type) ID {
 }
 
 func (w *World) nextArchetype(filter Filter, index int) (int, archetypeIter, bool) {
-	len := w.archetypes.Len()
+	len := int(w.archetypes.Len())
 	if index >= len {
 		panic("exceeded end of query")
 	}
diff --git a/examples/generic/main.go b/examples/generic/main.go
index 64306bf..72d62e3 100644
--- a/examples/generic/main.go
+++ b/examples/generic/main.go
@@ -2,7 +2,6 @@
 package main
 
 import (
-	"fmt"
 	"math/rand"
 
 	"github.com/mlange-42/arche/ecs"
@@ -83,5 +82,4 @@ func main() {
 		pos.X += vel.X
 		pos.Y += vel.Y
 	}
-	fmt.Println(world.Stats().String())
 }


Test patch:
diff --git a/ecs/archetype_test.go b/ecs/archetype_test.go
index 9de0822..e2d1ac5 100644
--- a/ecs/archetype_test.go
+++ b/ecs/archetype_test.go
@@ -111,7 +111,7 @@ func BenchmarkArchetypeAccess_1000(b *testing.B) {
 		len := int(arch.Len())
 		id := ID(0)
 		for j := 0; j < len; j++ {
-			pos := (*testStruct0)(arch.Get(i, id))
+			pos := (*testStruct0)(arch.Get(uint32(i), id))
 			_ = pos
 		}
 	}
@@ -138,7 +138,7 @@ func BenchmarkArchetypeAccessUnsafe_1000(b *testing.B) {
 		len := int(arch.Len())
 		id := ID(0)
 		for j := 0; j < len; j++ {
-			pos := (*testStruct0)(arch.GetUnsafe(i, id))
+			pos := (*testStruct0)(arch.GetUnsafe(uint32(i), id))
 			_ = pos
 		}
 	}