From aa63051db3eef6f883dfdbe3e7c65ff4423dfd05 Mon Sep 17 00:00:00 2001 From: Ukendio Date: Sun, 21 Dec 2025 19:21:42 +0100 Subject: [PATCH] Optimize idr_r removal by 35% --- src/jecs.luau | 15 +++++++++++-- test/tests.luau | 57 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/jecs.luau b/src/jecs.luau index 2045474..a3faf79 100755 --- a/src/jecs.luau +++ b/src/jecs.luau @@ -3246,22 +3246,33 @@ local function world_new() local records = idr_r.records for archetype_id in archetype_ids do local idr_r_archetype = archetypes[archetype_id] - local node = idr_r_archetype + -- local node = idr_r_archetype local entities = idr_r_archetype.entities local tr = records[archetype_id] local tr_count = counts[archetype_id] local idr_r_types = idr_r_archetype.types + local dst = table.clone(idr_r_types) for i = tr, tr + tr_count - 1 do local id = idr_r_types[i] - node = archetype_traverse_remove(world, id, node) + local at = table.find(dst, id) + if at then + table.remove(dst, at) + end + -- node = archetype_traverse_remove(world, id, node) local on_remove = component_index[id].on_remove if on_remove then + -- NOTE(marcus): Since hooks can move the entities + -- assumptions about which archetype it jumps to is + -- diminished. We assume that people who delete + -- relation will never have hooks on them. for _, entity in entities do on_remove(entity, id) end end end + local node = archetype_ensure(world, dst) + for i = #entities, 1, -1 do local e = entities[i] local r = entity_index_try_get_unsafe(e) :: record diff --git a/test/tests.luau b/test/tests.luau index b4d6c55..5133342 100755 --- a/test/tests.luau +++ b/test/tests.luau @@ -24,6 +24,59 @@ type Id = jecs.Id local entity_visualiser = require("@modules/entity_visualiser") local dwi = entity_visualiser.stringify +TEST("optimize idr_r removal", function() + + local pair = jecs.pair + local world = jecs.world() + local rel = world:component() + local A = world:component() + local B = world:component() + + local t1 = world:entity() + local t2 = world:entity() + + local entities = {} :: { jecs.Entity } + + for i = 1, 10 do + + local e1 = world:entity() + local e2 = world:entity() + + world:set(e1, A, true) + world:set(e2, A, true) + world:add(e1, pair(B, t1)) + world:add(e1, pair(B, t2)) + world:add(e2, pair(B, t1)) + world:add(e2, pair(B, t2)) + + table.insert(entities, e1) + table.insert(entities, e2) + end + + local e1 = world:entity() + local e2 = world:entity() + + table.insert(entities, e1) + table.insert(entities, e2) + + world:set(e1, A, true) + world:set(e2, A, true) + world:add(e1, pair(B, t1)) + world:add(e1, pair(B, t2)) + world:add(e2, pair(B, t1)) + world:add(e2, pair(B, t2)) + + BENCH("delete B", function() + world:delete(B) + end) + + for _, e in entities do + CHECK(world:has(e, A)) + CHECK(not world:target(e, B)) + CHECK(not world:target(e, B)) + end + +end) TEST("deleting t1's archetype before invoking its onremove hooks", function() local pair = jecs.pair local world = jecs.world() @@ -96,10 +149,6 @@ TEST("reproduce idr_t nil archetype bug", function() -- Removing this, but keeping Animator on the other 2 causes it to stop happening world:set(trackEntity1, cts.VelocitizeAnimationWeight, 0) - print("delete root") - world:delete(root) - print("---") - for entityId, info in trackedEntities do if world:contains(entityId) and not world:parent(entityId :: any) then print(`bugged entity found: {entityId}`)