Hacker News new | ask | show | jobs
by Rayhem 316 days ago
If you're really accessing those fields it's overwhelmingly likely that you're accessing the same field for all of your instances. Your access pattern is going to be

    monsters[0].health = //calculation
    monsters[1].health = //calculation
    monsters[2].health = //calculation
and not

    monsters[0].health = //calculation
    monsters[1].name = //update
    monsters[2].is_poisoned = //check
Striding over your monsters array means you have to load each monster to access one field which just blows your cache to hell. Much better is to pack all of your health values together in a structures-of-arrays layout as opposed to the usual arrays-of-structures approach here. Entity-component-system architectures are a brilliant tool for managing this with some pretty performant results.
1 comments

Could you give some intuition on why this is likely to be the case more frequently than accessing different values from a single entity?

I'm not a games developer, so I don't have a good feel for why you'd need to iterate over every monster's health property in a tightly loop vs accessing each monster and calculating their position, what properties they have and updating one or more of their values in an iteration.

Sure. If your monsters have health, then their health is going to need updating pretty much by definition. Option 1 would be to make each entity responsible for updating itself with an overloaded `update` function in sort of a "classical OOP" fashion:

    for(auto &entity: entities) {
        entity.update();
    }
You're then responsible for weaving the health changes into your update (or getting it from your parent). Weird stuff can happen if one entity updates before another, too: you likely want all of the health updates to occur after all of the damaging events. So now this update function has to register a deferred health update to occur down the line.

Option 2 with something like an Entity-Component-System design would have a "Health" system that handles health updates across the board.

    for(auto &damageEntity: entitiesThatDoDamage) {
        damageEntity.registerDamage();
    }

    for(auto &healthEntity: entitiesWithHealth) {
        healthEntity.updateHealth();
    }
Neither of these options is strictly better than the other. Option 1 is good if you need a lot of bespoke logic for health updates (dragons update differently from humans which are different from vehicles etc.). Option 2 is better if everyone has `newHealth = oldHealth - damage`. Things are generally more similar than they are different, so you end up changing all values of a single "kind" (structures-of-arrays) more often.
So, in option two, you'd be using SoA with a

  double health[];
which is the value that would be updated by healthEntity.updateHealth() ? So because you're sequentially updating only that attribute it should lend itself to being cache friendly and potentially prefetch prediction? (can't remember the term)
Yes, exactly. If you have to update everyone's health (you do) and the updates are pretty much the same (they are), then things don't get any more cache friendly than this.