slightly improve required fragments perf

This commit is contained in:
BlackMATov
2025-09-24 18:16:01 +07:00
parent 0aa57f6b5b
commit 9221da6ea7
3 changed files with 80 additions and 50 deletions

View File

@@ -2,7 +2,6 @@
## Backlog
- Improve the performance of required fragments by caching first-level required chunks.
- Improve the performance of builders that are used multiple times by caching hint chunks.
- Queries can cache major chunks to avoid finding them every time.
- observers and events
@@ -14,3 +13,7 @@
- We can return deferred status from modifying operations and spawn/clone methods.
- Should we make one builder:build method instead of :spawn and :clone?
## Known Issues
- Required fragments are slower than they should be

View File

@@ -9,10 +9,6 @@ local F1, F2, F3, F4, F5 = evo.id(5)
local Q1 = evo.builder():include(F1):spawn()
local B1 = evo.builder()
local B3 = evo.builder()
local B5 = evo.builder()
local R1 = evo.builder():require(F1):spawn()
local R3 = evo.builder():require(F1, F2, F3):spawn()
local R5 = evo.builder():require(F1, F2, F3, F4, F5):spawn()
@@ -103,11 +99,10 @@ print '----------------------------------------'
basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entities with 1 component', N),
function()
local set, spawn = B1.set, B1.spawn
local builder = evo.builder()
for _ = 1, N do
set(B1, F1)
spawn(B1)
builder:set(F1):spawn()
end
evo.batch_destroy(Q1)
@@ -115,13 +110,10 @@ basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entiti
basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entities with 3 components', N),
function()
local set, spawn = B3.set, B3.spawn
local builder = evo.builder()
for _ = 1, N do
set(B3, F1)
set(B3, F2)
set(B3, F3)
spawn(B3)
builder:set(F1):set(F2):set(F3):spawn()
end
evo.batch_destroy(Q1)
@@ -129,15 +121,10 @@ basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entiti
basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entities with 5 components', N),
function()
local set, spawn = B5.set, B5.spawn
local builder = evo.builder()
for _ = 1, N do
set(B5, F1)
set(B5, F2)
set(B5, F3)
set(B5, F4)
set(B5, F5)
spawn(B5)
builder:set(F1):set(F2):set(F3):set(F4):set(F5):spawn()
end
evo.batch_destroy(Q1)
@@ -147,11 +134,10 @@ print '----------------------------------------'
basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entities with 1 required component', N),
function()
local set, spawn = B1.set, B1.spawn
local builder = evo.builder()
for _ = 1, N do
set(B1, R1)
spawn(B1)
builder:set(R1):spawn()
end
evo.batch_destroy(Q1)
@@ -159,11 +145,10 @@ basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entiti
basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entities with 3 required components', N),
function()
local set, spawn = B3.set, B3.spawn
local builder = evo.builder()
for _ = 1, N do
set(B3, R3)
spawn(B3)
builder:set(R3):spawn()
end
evo.batch_destroy(Q1)
@@ -171,11 +156,10 @@ basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entiti
basics.describe_bench(string.format('Spawn Benchmarks: Builder Spawn | %d entities with 5 required components', N),
function()
local set, spawn = B5.set, B5.spawn
local builder = evo.builder()
for _ = 1, N do
set(B5, R5)
spawn(B5)
builder:set(R5):spawn()
end
evo.batch_destroy(Q1)

View File

@@ -1683,26 +1683,44 @@ function __chunk_get_all_components(chunk, place, ...)
end
end
---@param chunk evolved.chunk
---@param ini_chunk evolved.chunk
---@param req_fragment_set table<evolved.fragment, integer>
---@param req_fragment_list evolved.fragment[]
---@param req_fragment_count integer
---@return integer
---@nodiscard
function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_list, req_fragment_count)
function __chunk_required_fragments(ini_chunk, req_fragment_set, req_fragment_list, req_fragment_count)
---@type evolved.fragment[]
local fragment_stack = __acquire_table(__table_pool_tag.fragment_list)
local fragment_stack_size = 0
do
local chunk_fragment_list = chunk.__fragment_list
local chunk_fragment_count = chunk.__fragment_count
local ini_fragment_set = ini_chunk.__fragment_set
local ini_fragment_list = ini_chunk.__fragment_list
local ini_fragment_count = ini_chunk.__fragment_count
__lua_table_move(
chunk_fragment_list, 1, chunk_fragment_count,
fragment_stack_size + 1, fragment_stack)
for ini_fragment_index = 1, ini_fragment_count do
local ini_fragment = ini_fragment_list[ini_fragment_index]
fragment_stack_size = fragment_stack_size + chunk_fragment_count
local ini_fragment_requires = __sorted_requires[ini_fragment]
local ini_fragment_require_list = ini_fragment_requires and ini_fragment_requires.__item_list
local ini_fragment_require_count = ini_fragment_requires and ini_fragment_requires.__item_count or 0
for required_fragment_index = 1, ini_fragment_require_count do
local required_fragment = ini_fragment_require_list[required_fragment_index]
if ini_fragment_set[required_fragment] or req_fragment_set[required_fragment] then
-- this fragment has already been gathered
else
req_fragment_count = req_fragment_count + 1
req_fragment_set[required_fragment] = req_fragment_count
req_fragment_list[req_fragment_count] = required_fragment
if __sorted_requires[required_fragment] then
fragment_stack_size = fragment_stack_size + 1
fragment_stack[fragment_stack_size] = required_fragment
end
end
end
end
while fragment_stack_size > 0 do
@@ -1718,15 +1736,17 @@ function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_list,
for fragment_require_index = 1, fragment_require_count do
local required_fragment = fragment_require_list[fragment_require_index]
if req_fragment_set[required_fragment] then
if ini_fragment_set[required_fragment] or req_fragment_set[required_fragment] then
-- this fragment has already been gathered
else
req_fragment_count = req_fragment_count + 1
req_fragment_set[required_fragment] = req_fragment_count
req_fragment_list[req_fragment_count] = required_fragment
fragment_stack_size = fragment_stack_size + 1
fragment_stack[fragment_stack_size] = required_fragment
if __sorted_requires[required_fragment] then
fragment_stack_size = fragment_stack_size + 1
fragment_stack[fragment_stack_size] = required_fragment
end
end
end
end
@@ -1735,20 +1755,41 @@ function __chunk_required_fragments(chunk, req_fragment_set, req_fragment_list,
return req_fragment_count
end
---@param fragment evolved.fragment
---@param ini_chunk evolved.chunk
---@param ini_fragment evolved.fragment
---@param req_fragment_set table<evolved.fragment, integer>
---@param req_fragment_list evolved.fragment[]
---@param req_fragment_count integer
---@return integer
---@nodiscard
function __fragment_required_fragments(fragment, req_fragment_set, req_fragment_list, req_fragment_count)
function __fragment_required_fragments(ini_chunk, ini_fragment, req_fragment_set, req_fragment_list, req_fragment_count)
---@type evolved.fragment[]
local fragment_stack = __acquire_table(__table_pool_tag.fragment_list)
local fragment_stack_size = 0
local ini_fragment_set = ini_chunk.__fragment_set
do
fragment_stack_size = fragment_stack_size + 1
fragment_stack[fragment_stack_size] = fragment
local ini_fragment_requires = __sorted_requires[ini_fragment]
local ini_fragment_require_list = ini_fragment_requires and ini_fragment_requires.__item_list
local ini_fragment_require_count = ini_fragment_requires and ini_fragment_requires.__item_count or 0
for required_fragment_index = 1, ini_fragment_require_count do
local required_fragment = ini_fragment_require_list[required_fragment_index]
if ini_fragment_set[required_fragment] or req_fragment_set[required_fragment] then
-- this fragment has already been gathered
else
req_fragment_count = req_fragment_count + 1
req_fragment_set[required_fragment] = req_fragment_count
req_fragment_list[req_fragment_count] = required_fragment
if __sorted_requires[required_fragment] then
fragment_stack_size = fragment_stack_size + 1
fragment_stack[fragment_stack_size] = required_fragment
end
end
end
end
while fragment_stack_size > 0 do
@@ -1764,15 +1805,17 @@ function __fragment_required_fragments(fragment, req_fragment_set, req_fragment_
for fragment_require_index = 1, fragment_require_count do
local required_fragment = fragment_require_list[fragment_require_index]
if req_fragment_set[required_fragment] then
if ini_fragment_set[required_fragment] or req_fragment_set[required_fragment] then
-- this fragment has already been gathered
else
req_fragment_count = req_fragment_count + 1
req_fragment_set[required_fragment] = req_fragment_count
req_fragment_list[req_fragment_count] = required_fragment
fragment_stack_size = fragment_stack_size + 1
fragment_stack[fragment_stack_size] = required_fragment
if __sorted_requires[required_fragment] then
fragment_stack_size = fragment_stack_size + 1
fragment_stack[fragment_stack_size] = required_fragment
end
end
end
end
@@ -3155,7 +3198,7 @@ function __chunk_set(old_chunk, fragment, component)
---@type evolved.fragment[]
req_fragment_list = __acquire_table(__table_pool_tag.fragment_list)
req_fragment_count = __fragment_required_fragments(fragment,
req_fragment_count = __fragment_required_fragments(ini_new_chunk, fragment,
req_fragment_set, req_fragment_list, req_fragment_count)
for req_fragment_index = 1, req_fragment_count do
@@ -5133,7 +5176,7 @@ function __evolved_set(entity, fragment, component)
---@type evolved.fragment[]
req_fragment_list = __acquire_table(__table_pool_tag.fragment_list)
req_fragment_count = __fragment_required_fragments(fragment,
req_fragment_count = __fragment_required_fragments(ini_new_chunk, fragment,
req_fragment_set, req_fragment_list, req_fragment_count)
for req_fragment_index = 1, req_fragment_count do