non-shrinking version of garbage collection

This commit is contained in:
BlackMATov
2026-02-02 20:46:50 +07:00
parent a1440f26df
commit d1105c15ad
10 changed files with 62 additions and 40 deletions

View File

@@ -55,6 +55,7 @@
- [Fragment Requirements](#fragment-requirements)
- [Destruction Policies](#destruction-policies)
- [Custom Component Storages](#custom-component-storages)
- [Garbage Collection](#garbage-collection)
- [Cheat Sheet](#cheat-sheet)
- [Aliases](#aliases)
- [Predefs](#predefs)
@@ -1346,6 +1347,24 @@ evolved.builder()
evolved.process_with(MOVEMENT_SYSTEM, 0.016)
```
### Garbage Collection
While using the library, some internal data structures can become obsolete and should be cleaned up to free memory. For example, empty chunks that no longer contain entities can be removed. Component storages can also have unused capacity that can be shrunk to save memory. The library provides a function to control this garbage collection process.
```lua
---@param no_shrink? boolean
function evolved.collect_garbage(no_shrink) end
```
By default, [`evolved.collect_garbage`](#evolvedcollect_garbage) cleans up obsolete data structures and shrinks component storages to fit their current size. If you pass `true`, it only cleans up obsolete data structures and skips shrinking. This avoids the overhead of resizing storages, which can be expensive.
Call this function periodically to keep memory usage under control. It is best to call it between levels or during loading screens when performance is not critical. Also, call Lua's built-in garbage collector afterward to ensure all unused memory is freed.
```lua
evolved.collect_garbage()
collectgarbage('collect')
```
## Cheat Sheet
### Aliases
@@ -1481,7 +1500,7 @@ process :: system... -> ()
process_with :: system, ... -> ()
debug_mode :: boolean -> ()
collect_garbage :: ()
collect_garbage :: boolean? -> ()
```
### Classes
@@ -2007,7 +2026,8 @@ function evolved.debug_mode(yesno) end
### `evolved.collect_garbage`
```lua
function evolved.collect_garbage() end
---@param no_shrink? boolean
function evolved.collect_garbage(no_shrink) end
```
## Classes

View File

@@ -9,7 +9,6 @@
## Thoughts
- We should have a way to not copy components on deferred spawn/clone
- Having a light version of the gargabe collector can be useful for some use-cases
- Basic default component value as true looks awful, should we use something else?
## Known Issues

View File

@@ -118,11 +118,11 @@ end
---
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end
evo.destroy(__table_unpack(all_entity_list))
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end

View File

@@ -124,11 +124,11 @@ end
---
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end
evo.destroy(__table_unpack(all_entity_list))
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end

View File

@@ -296,7 +296,7 @@ end
---
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end
evo.destroy(__table_unpack(all_query_list))
@@ -304,5 +304,5 @@ evo.destroy(__table_unpack(all_entity_list))
evo.destroy(__table_unpack(all_fragment_list))
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end

View File

@@ -78,11 +78,11 @@ end
---
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end
evo.destroy(__table_unpack(all_entity_list))
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end

View File

@@ -115,17 +115,17 @@ end
if math.random(1, 2) == 1 then
evo.destroy(__table_unpack(all_entity_list))
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end
evo.destroy(__table_unpack(all_fragment_list))
else
evo.destroy(__table_unpack(all_fragment_list))
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end
evo.destroy(__table_unpack(all_entity_list))
end
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end

View File

@@ -64,11 +64,11 @@ end
---
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end
evo.destroy(__table_unpack(all_entity_list))
if math.random(1, 2) == 1 then
evo.collect_garbage()
evo.collect_garbage(math.random(1, 2) == 1)
end

View File

@@ -212,7 +212,7 @@
process_with: function(system: System, ...: any)
debug_mode: function(yesno: boolean)
collect_garbage: function()
collect_garbage: function(no_shrink?: boolean)
chunk: function(fragment: Fragment, ...: Fragment): Chunk, { Entity }, integer
builder: function(): Builder

View File

@@ -6145,7 +6145,8 @@ function __evolved_debug_mode(yesno)
__debug_mode = yesno
end
function __evolved_collect_garbage()
---@param no_shrink boolean?
function __evolved_collect_garbage(no_shrink)
if __defer_depth > 0 then
__defer_call_hook(__evolved_collect_garbage)
return
@@ -6209,16 +6210,16 @@ function __evolved_collect_garbage()
local postorder_chunk_entity_count = postorder_chunk.__entity_count
local postorder_chunk_entity_capacity = postorder_chunk.__entity_capacity
local should_be_purged =
local can_be_purged =
postorder_chunk_child_count == 0 and
postorder_chunk_entity_count == 0
local should_be_shrunk =
local can_be_shrunk =
postorder_chunk_entity_count < postorder_chunk_entity_capacity
if should_be_purged then
if can_be_purged then
__purge_chunk(postorder_chunk)
elseif should_be_shrunk then
elseif can_be_shrunk and not no_shrink then
__shrink_chunk(postorder_chunk, 0)
end
end
@@ -6234,29 +6235,31 @@ function __evolved_collect_garbage()
end
end
for table_pool_tag = 1, __table_pool_tag.__count do
local table_pool_reserve = __table_pool_reserve[table_pool_tag]
if not no_shrink then
for table_pool_tag = 1, __table_pool_tag.__count do
local table_pool_reserve = __table_pool_reserve[table_pool_tag]
---@type evolved.table_pool
local new_table_pool = __lua_table_new(table_pool_reserve)
---@type evolved.table_pool
local new_table_pool = __lua_table_new(table_pool_reserve)
for table_pool_index = 1, table_pool_reserve do
new_table_pool[table_pool_index] = {}
for table_pool_index = 1, table_pool_reserve do
new_table_pool[table_pool_index] = {}
end
new_table_pool.__size = table_pool_reserve
__tagged_table_pools[table_pool_tag] = new_table_pool
end
new_table_pool.__size = table_pool_reserve
do
__entity_chunks = __table_dup(__entity_chunks)
__entity_places = __table_dup(__entity_places)
end
__tagged_table_pools[table_pool_tag] = new_table_pool
end
do
__entity_chunks = __table_dup(__entity_chunks)
__entity_places = __table_dup(__entity_places)
end
do
__defer_points = __list_dup(__defer_points, __defer_depth)
__defer_bytecode = __list_dup(__defer_bytecode, __defer_length)
do
__defer_points = __list_dup(__defer_points, __defer_depth)
__defer_bytecode = __list_dup(__defer_bytecode, __defer_length)
end
end
__evolved_commit()