mirror of
https://github.com/Ukendio/jecs.git
synced 2025-06-20 08:19:18 +00:00
Less memory footprint
This commit is contained in:
parent
8bea43a9fc
commit
85a970e9ff
5 changed files with 1089 additions and 579 deletions
3
.luaurc
3
.luaurc
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"jecs": "src",
|
"jecs": "src",
|
||||||
"testkit": "testkit"
|
"testkit": "testkit",
|
||||||
|
"mirror": "mirror"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"ReplicatedStorage": {
|
"ReplicatedStorage": {
|
||||||
"$className": "ReplicatedStorage",
|
"$className": "ReplicatedStorage",
|
||||||
"Lib": {
|
"Lib": {
|
||||||
"$path": "lib"
|
"$path": "src"
|
||||||
},
|
},
|
||||||
"rgb": {
|
"rgb": {
|
||||||
"$path": "rgb.luau"
|
"$path": "rgb.luau"
|
||||||
|
|
1096
mirror/init.luau
1096
mirror/init.luau
File diff suppressed because it is too large
Load diff
137
src/init.luau
137
src/init.luau
|
@ -688,24 +688,22 @@ local function replaceMult(row, columns, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function preparedQuery(compatibleArchetypes: { Archetype },
|
local query: (World, ...i53) -> Query
|
||||||
components: { i53? }, indices: { { number } })
|
do
|
||||||
|
local indices: { { number } }
|
||||||
|
local compatibleArchetypes: { Archetype }
|
||||||
|
local length
|
||||||
|
local components: { number }
|
||||||
|
local queryLength: number
|
||||||
|
local lastArchetype: number
|
||||||
|
local archetype: Archetype
|
||||||
|
|
||||||
local queryLength = #components
|
local queryOutput: { any }
|
||||||
|
|
||||||
local lastArchetype = 1
|
local entities: {}
|
||||||
local archetype: Archetype = compatibleArchetypes[lastArchetype]
|
local i: number
|
||||||
|
|
||||||
if not archetype then
|
local function query_next()
|
||||||
return EmptyQuery
|
|
||||||
end
|
|
||||||
|
|
||||||
local queryOutput = {}
|
|
||||||
|
|
||||||
local entities = archetype.entities
|
|
||||||
local i = #entities
|
|
||||||
|
|
||||||
local function queryNext(): ...any
|
|
||||||
local entityId = entities[i]
|
local entityId = entities[i]
|
||||||
while entityId == nil do
|
while entityId == nil do
|
||||||
lastArchetype += 1
|
lastArchetype += 1
|
||||||
|
@ -735,35 +733,19 @@ local function preparedQuery(compatibleArchetypes: { Archetype },
|
||||||
elseif queryLength == 4 then
|
elseif queryLength == 4 then
|
||||||
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row]
|
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row]
|
||||||
elseif queryLength == 5 then
|
elseif queryLength == 5 then
|
||||||
return entityId,
|
return entityId,columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row],
|
||||||
columns[tr[1]][row],
|
|
||||||
columns[tr[2]][row],
|
|
||||||
columns[tr[3]][row],
|
|
||||||
columns[tr[4]][row],
|
|
||||||
columns[tr[5]][row]
|
columns[tr[5]][row]
|
||||||
elseif queryLength == 6 then
|
elseif queryLength == 6 then
|
||||||
return entityId,
|
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row],
|
||||||
columns[tr[1]][row],
|
|
||||||
columns[tr[2]][row],
|
|
||||||
columns[tr[3]][row],
|
|
||||||
columns[tr[4]][row],
|
|
||||||
columns[tr[5]][row],
|
columns[tr[5]][row],
|
||||||
columns[tr[6]][row]
|
columns[tr[6]][row]
|
||||||
elseif queryLength == 7 then
|
elseif queryLength == 7 then
|
||||||
return entityId,
|
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row],
|
||||||
columns[tr[1]][row],
|
|
||||||
columns[tr[2]][row],
|
|
||||||
columns[tr[3]][row],
|
|
||||||
columns[tr[4]][row],
|
|
||||||
columns[tr[5]][row],
|
columns[tr[5]][row],
|
||||||
columns[tr[6]][row],
|
columns[tr[6]][row],
|
||||||
columns[tr[7]][row]
|
columns[tr[7]][row]
|
||||||
elseif queryLength == 8 then
|
elseif queryLength == 8 then
|
||||||
return entityId,
|
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row],
|
||||||
columns[tr[1]][row],
|
|
||||||
columns[tr[2]][row],
|
|
||||||
columns[tr[3]][row],
|
|
||||||
columns[tr[4]][row],
|
|
||||||
columns[tr[5]][row],
|
columns[tr[5]][row],
|
||||||
columns[tr[6]][row],
|
columns[tr[6]][row],
|
||||||
columns[tr[7]][row],
|
columns[tr[7]][row],
|
||||||
|
@ -777,7 +759,7 @@ local function preparedQuery(compatibleArchetypes: { Archetype },
|
||||||
return entityId, unpack(queryOutput, 1, queryLength)
|
return entityId, unpack(queryOutput, 1, queryLength)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function without(self, ...): Query
|
local function query_without(self, ...): Query
|
||||||
local withoutComponents = { ... }
|
local withoutComponents = { ... }
|
||||||
for i = #compatibleArchetypes, 1, -1 do
|
for i = #compatibleArchetypes, 1, -1 do
|
||||||
local archetype = compatibleArchetypes[i]
|
local archetype = compatibleArchetypes[i]
|
||||||
|
@ -803,16 +785,16 @@ local function preparedQuery(compatibleArchetypes: { Archetype },
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
local function iter()
|
local function query_iter()
|
||||||
lastArchetype = 1
|
lastArchetype = 1
|
||||||
archetype = compatibleArchetypes[1]
|
archetype = compatibleArchetypes[1]
|
||||||
entities = archetype.entities
|
entities = archetype.entities
|
||||||
i = #entities
|
i = #entities
|
||||||
|
|
||||||
return queryNext
|
return query_next
|
||||||
end
|
end
|
||||||
|
|
||||||
local function replace(_, fn: any)
|
local function query_replace(_, fn: any)
|
||||||
for i, archetype in compatibleArchetypes do
|
for i, archetype in compatibleArchetypes do
|
||||||
local tr = indices[i]
|
local tr = indices[i]
|
||||||
local columns = archetype.columns
|
local columns = archetype.columns
|
||||||
|
@ -852,29 +834,18 @@ local function preparedQuery(compatibleArchetypes: { Archetype },
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local it = {
|
function query(world: World, ...: number): Query
|
||||||
__iter = iter,
|
|
||||||
next = queryNext,
|
|
||||||
without = without,
|
|
||||||
replace = replace
|
|
||||||
}
|
|
||||||
|
|
||||||
return setmetatable(it, it) :: any
|
|
||||||
end
|
|
||||||
|
|
||||||
local function query(world: World, ...: number): Query
|
|
||||||
-- breaking?
|
-- breaking?
|
||||||
if (...) == nil then
|
if (...) == nil then
|
||||||
error("Missing components")
|
error("Missing components")
|
||||||
end
|
end
|
||||||
|
|
||||||
local indices: { { number } } = {}
|
indices = {}
|
||||||
local compatibleArchetypes: { Archetype } = {}
|
compatibleArchetypes = {}
|
||||||
local length = 0
|
length = 0
|
||||||
|
components = { ... }
|
||||||
|
|
||||||
local components: { number } = { ... }
|
|
||||||
local archetypes: { Archetype } = world.archetypes :: any
|
local archetypes: { Archetype } = world.archetypes :: any
|
||||||
|
|
||||||
local firstArchetypeMap: ArchetypeMap
|
local firstArchetypeMap: ArchetypeMap
|
||||||
local componentIndex = world.componentIndex
|
local componentIndex = world.componentIndex
|
||||||
|
|
||||||
|
@ -890,8 +861,8 @@ local function query(world: World, ...: number): Query
|
||||||
end
|
end
|
||||||
|
|
||||||
for id in firstArchetypeMap.cache do
|
for id in firstArchetypeMap.cache do
|
||||||
local archetype = archetypes[id]
|
local compatibleArchetype = archetypes[id]
|
||||||
local archetypeRecords = archetype.records
|
local archetypeRecords = compatibleArchetype.records
|
||||||
|
|
||||||
local records: { number } = {}
|
local records: { number } = {}
|
||||||
local skip = false
|
local skip = false
|
||||||
|
@ -911,11 +882,32 @@ local function query(world: World, ...: number): Query
|
||||||
end
|
end
|
||||||
|
|
||||||
length += 1
|
length += 1
|
||||||
compatibleArchetypes[length] = archetype
|
compatibleArchetypes[length] = compatibleArchetype
|
||||||
indices[length] = records
|
indices[length] = records
|
||||||
end
|
end
|
||||||
|
|
||||||
return preparedQuery(compatibleArchetypes, components, indices)
|
lastArchetype = 1
|
||||||
|
archetype = compatibleArchetypes[lastArchetype]
|
||||||
|
|
||||||
|
if not archetype then
|
||||||
|
return EmptyQuery
|
||||||
|
end
|
||||||
|
|
||||||
|
queryOutput = {}
|
||||||
|
queryLength = #components
|
||||||
|
|
||||||
|
entities = archetype.entities
|
||||||
|
i = #entities
|
||||||
|
|
||||||
|
local it = {
|
||||||
|
__iter = query_iter,
|
||||||
|
next = query_next,
|
||||||
|
without = query_without,
|
||||||
|
replace = query_replace
|
||||||
|
}
|
||||||
|
|
||||||
|
return setmetatable(it, it) :: any
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
type WorldIterator = (() -> (i53, { [unknown]: unknown? })) & (() -> ()) & (() -> i53)
|
type WorldIterator = (() -> (i53, { [unknown]: unknown? })) & (() -> ()) & (() -> i53)
|
||||||
|
@ -1055,6 +1047,18 @@ export type WorldShim = typeof(setmetatable(
|
||||||
local World = {}
|
local World = {}
|
||||||
World.__index = World
|
World.__index = World
|
||||||
|
|
||||||
|
World.entity = entity
|
||||||
|
World.query = query
|
||||||
|
World.remove = remove
|
||||||
|
World.clear = clear
|
||||||
|
World.delete = delete
|
||||||
|
World.component = newComponent
|
||||||
|
World.add = add
|
||||||
|
World.set = set
|
||||||
|
World.get = get
|
||||||
|
World.target = target
|
||||||
|
World.parent = parent
|
||||||
|
|
||||||
function World.new()
|
function World.new()
|
||||||
local self = setmetatable({
|
local self = setmetatable({
|
||||||
archetypeIndex = {} :: { [string]: Archetype },
|
archetypeIndex = {} :: { [string]: Archetype },
|
||||||
|
@ -1072,6 +1076,7 @@ function World.new()
|
||||||
nextEntityId = 0,
|
nextEntityId = 0,
|
||||||
ROOT_ARCHETYPE = (nil :: any) :: Archetype,
|
ROOT_ARCHETYPE = (nil :: any) :: Archetype,
|
||||||
}, World)
|
}, World)
|
||||||
|
|
||||||
self.ROOT_ARCHETYPE = archetypeOf(self, {})
|
self.ROOT_ARCHETYPE = archetypeOf(self, {})
|
||||||
|
|
||||||
-- Initialize built-in components
|
-- Initialize built-in components
|
||||||
|
@ -1080,20 +1085,8 @@ function World.new()
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
World.entity = entity
|
|
||||||
World.query = query
|
|
||||||
World.remove = remove
|
|
||||||
World.clear = clear
|
|
||||||
World.delete = delete
|
|
||||||
World.component = newComponent
|
|
||||||
World.add = add
|
|
||||||
World.set = set
|
|
||||||
World.get = get
|
|
||||||
World.target = target
|
|
||||||
World.parent = parent
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
World = World :: { new: () -> WorldShim },
|
World = World :: { new: () -> WorldShim } ,
|
||||||
|
|
||||||
OnAdd = EcsOnAdd :: Entity,
|
OnAdd = EcsOnAdd :: Entity,
|
||||||
OnRemove = EcsOnRemove :: Entity,
|
OnRemove = EcsOnRemove :: Entity,
|
||||||
|
|
56
test/leaky.luau
Normal file
56
test/leaky.luau
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
|
||||||
|
local function calculateAverage(times)
|
||||||
|
local sum = 0
|
||||||
|
for _, time in ipairs(times) do
|
||||||
|
sum = sum + time
|
||||||
|
end
|
||||||
|
return sum / #times
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Main logic to time the test function
|
||||||
|
|
||||||
|
local CASES = {
|
||||||
|
jecs = function(world, ...)
|
||||||
|
for i = 1, 100 do
|
||||||
|
local q = world:query(...)
|
||||||
|
for _ in q do end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
mirror = function(world, ...)
|
||||||
|
for i = 1, 100 do
|
||||||
|
local q = world:query(...)
|
||||||
|
for _ in q do end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, fn in CASES do
|
||||||
|
local times = {}
|
||||||
|
local allocations = {}
|
||||||
|
local ecs = require("@"..name)
|
||||||
|
local world = ecs.World.new()
|
||||||
|
local A, B, C = world:component(), world:component(), world:component()
|
||||||
|
|
||||||
|
for i = 1, 5 do
|
||||||
|
local e = world:entity()
|
||||||
|
world:add(e, A)
|
||||||
|
world:add(e, B)
|
||||||
|
world:add(e, C)
|
||||||
|
end
|
||||||
|
|
||||||
|
collectgarbage("collect")
|
||||||
|
local count = collectgarbage("count")
|
||||||
|
|
||||||
|
for i = 1, 50000 do
|
||||||
|
local startTime = os.clock()
|
||||||
|
fn(world, A, B, C)
|
||||||
|
local allocated = collectgarbage("count")
|
||||||
|
collectgarbage("collect")
|
||||||
|
local endTime = os.clock()
|
||||||
|
table.insert(times, endTime - startTime)
|
||||||
|
table.insert(allocations, allocated)
|
||||||
|
end
|
||||||
|
|
||||||
|
print(name, "gc cycle time", calculateAverage(times))
|
||||||
|
print(name, "memory allocated", calculateAverage(allocations))
|
||||||
|
end
|
Loading…
Reference in a new issue