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 :-

enter image description here

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.)

enter image description here

(edit) believe underlying cache miss occur different address between cannon inside same ship.

for example (@ address of turret component),

  • shipa has turret@01 turret@49
  • shipb has turret@50 turret@99

in later timesteps, if turret@99 moved shipa, :-

  • shipa has turret@01 turret@49 + turret@99 (mem jump)
  • shipb has turret@50 turret@98

enter image description here

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

Popular posts from this blog

node.js - Node js - Trying to send POST request, but it is not loading javascript content -

javascript - Replicate keyboard event with html button -

javascript - Web audio api 5.1 surround example not working in firefox -