Open TheWagi opened 2 months ago
Thank you for your very thoughtful comment, I'll do my best to answer your questions. Feel free to respond with more!
The pool is given an initial size and grows when necessary, I'm not clear on how splitting them into two groups would help. Please elaborate on this concept and how it could be implemented.
The data structures ecs_array_t
and ecs_stack_t
grew out of different use cases. They could easily be combined. A stack can be used to create a pool, which would require some sort of ecs_array_pop
function.
The ready flag is mostly used for debugging, however there are several other uses, which you indicated. For various reasons, I'm not happy with any of the alternatives you presented. I think I'll try a structure of arrays (SoA) like approach and see if that helps.
You should track issue #12 as I'm working on some bug fixes with another fellow.
I suppose if the entity id pool is initialized so that ids are popped off in order, this could reduce upfront memory usage.
Some updates in my thinking:
ecs_array_t
and ecs_stack_t
, but that is a low prioritycomp_bits
and the ready
variables into separate arrays. Unless I'm mistaken, this should eliminate the need for padding. I am, however, worried about the cache. I think it is unlikely to create performance problems in release mode, but I'll create a benchmark to be sure. If this doesn't pan out, I'll try storing the ready variable in the MSB of the comp_bits
as you suggested.I'll start working on these upgrades soon, but I have some bugs to squash at the moment. I should be able to get to these within a week or so.
Hello,
I've recently discovered your project through https://github.com/abeimler/ecs_benchmark while researching different solutions and implementation ideas for entity component systems. My remarks or questions are only about the ECS.
I've rewritten (renamed, reorganised) completely
pico_ecs.h
and its corresponding Test and Benchmark to accustom myself with it and also to match my own conventions, and I'm developing it in C++ on and for Windows, my compiler is x64 MSVC v19.29 with VS 16.11, so I've sadly not much code to share or commit. So far, I kept it as much C as possible, the only C++ features used areI reached a point where the next modifications planned are going to diverge from the original logic. Therefore, I thought it would be a good time to ask questions and or debate on some design choices, with hopefully some enlightenments on both parts.
I'm basing myself on the last available version at the time of writing, which is commit 433f25f
1. Why are you exclusively using a prepopulated entity identifier pool ?
Entity identifiers are simple unsigned integer values that are used as indices. You are only counting down when creating a new entity. The pool is useful when some entities are deleted and new ones are created, so that those new entities re-use identifiers and therefore keep the identifiers within the bounds of arrays as they used as indices. However, in most use cases this concerns only a portion of the entities, usually a lot exist from start to finish. Wouldn't it be best to split them in two groups ? This reduces the memory because the prepopulated pool of identifiers would be smaller and improve the creating and deletion speed by a bit of others entities.
2. Why are you using stacks ?
The three variables that are of type
ecs_stack_t
areentity_pool
,destroy_queue
, andremove_queue
All could be of typeecs_array_t
with the only logic modification being to count up instead of down forentity_pool
. Bothdestroy_queue
, andremove_queue
are iterated in memory order inecs_flush_destroyed
andecs_flush_removed
which is not what how a stack is meant to work, that is: first-in last-out, last-in first-out, to my knowledge. I suggest to useecs_array_t
instead, it reduces the amount of code, as the typeecs_stack_t
and it's associated functions are not required.3. Why does
ecs_entity_t
have aready
variable ?There is no other assignment than at creation in
ecs_create
. When you create an entity, you generate its identifier and set itready
. Then it is only referred in if statements inecs_free
which destroys the ECSecs_reset
which resset the ECSecs_is_ready
used inecs_flush_destroyed
andecs_flush_removed
ecs_is_entity_ready
used in assertsentities
contains allecs_entity_t
, it is only iterated inecs_reset
andecs_free
both of which should occur in permitting state, that is not during updates, most likely only when the game starts, changes scene, and terminates.ready
is generating padding because the structure is aligned to it's highest alignment variable. Let's say I havePICO_ECS_MAX_COMPONENTS
= 135 andentity_count
= 500000, in 64bit.entities
takes 16M bytes of which 12M bytes arecomp_bits
, 500K bytes areready
, and 3.5M bytes are padding caused byready
. This is 21.8% of padding memory, which of course varies with the value ofPICO_ECS_MAX_COMPONENTS
.The three solutions I see are
ready
into thecomp_bits
, that requires modifying a bit howecs_bitset_t
works to take into account that one bit itready
. Maybe unions ?ready
completely and loose a few microseconds when callingecs_reset
orecs_free
My results
Those are three of all the changes I made to increase the performance by 40 to 130% depending on what is benchmarked and still have some planned. I'm also planning on improving the tests and benchmarks.
I'm really interested in your takes, what I missed and got wrong.
Thank you
Wagi