c++ - avoid cache miss related to 1:N relationship in entity-component-system -
how avoid cache miss related 1:n (pirateship
-cannon
) relationship in entity-component-system (ecs)?
for example, pirateship
can have 1-100 cannons
. (1:n)
each cannon
can detach/attach freely pirateship
@ time.
for reasons, pirateship
, cannon
should entity.
memory diagram
in around first time-steps, when ship
/cannon
gradually created, ecs memory looks nice :-
image note:
- left = low address, right = high address
- although there seems gaps,
shipcom
,cannoncom
compact arrays.
it fast access cannon
information ship
, vice versa (pseudo code):-
ptr<shipcom> shipcom=....; entityptr ship = shipcom; //implicitly conversion array<entityptr> cannons = getallcannonfromship(ship); for(auto cannon: cannons){ ptr<cannoncom> cannoncom=cannon; //cannoncom-> .... }
problem
in later time-step, ship
/ cannon
randomly created/destroyed.
result, entity
,shipcom
, cannoncom
array has gap scattering around.
when want allocate them, random memory block pool.
entityptr ship = ..... (an old existing ship) entityptr cannon = createnewentity(); ptr<cannoncom> cannoncom= createnew<cannoncom>(cannon); attach_pirate_cannon(ship,cannon); //^ ship entity , cannon tend have different (far-away) address
thus, "really fast" code above become bottom-neck. (i profiled.)
(edit) believe underlying cache miss occur different address between cannon inside same ship.
for example (@
address of turret component),
shipa
hasturret@01
turret@49
shipb
hasturret@50
turret@99
in later timesteps, if turret@99
moved shipa
, :-
shipa
hasturret@01
turret@49
+turret@99
(mem jump)shipb
hasturret@50
turret@98
question
what design pattern / c++ magic reduce cache miss frequently-used relation?
more information:
- in real case, there lot of 1:1 , 1:n relationship. relationship binds type of component type of component.
- for example,
relation_pirate_cannon = (shipcom:cannoncom)
,relation_physic_graphic = (physiccom:graphiccom)
- only of relation "indirect" often.
- current architecture has no limit on amount of
entity
/shipcom
/cannoncom
.
don't want restrict in beginning of program. - i prefer improvement not make game-logic coding harder.
the first solution come mind enable relocation, believe last resort approach.
a possible solution add layer of indirection. slows things down bit, helps keeping compact arrays , speeding whole thing. profiling way know if helps.
that being said, how that?
here brief introduction sparse set , it's worth reading before proceed better understand i'm saying.
instead of create relationships between items within same array, use second array point.
let's call 2 arrays reverse , direct:
- reverse accessed through entity identifier (a number, index in array). each , every slot contains index within direct array.
- direct accessed, well... directly , each slot contains entity identifier (that index access reverse array) , actual component.
whenever add cannon, entity identifier , first free slot in direct array. set slot.entity
entity identifier , put in reverse[entity]
index of slot. whenever drop something, copy last element in direct array keep compact , adjust indexes relationships hold up.
ship store indexes outer array (reverse), free switch , forth things within inner array (direct).
what advantages , disadvantages?
well, whenever access cannons through outer array, have jump because of layer of indirection. anyway, long succeed in keeping low number of accesses made way , visit direct array in systems, have compact array iterate , lowest number of cache misses.
Comments
Post a Comment