Objet de tableaux ou tableau d'objets?

13

Je fais un jeu de simulation de gestion, quelque chose comme Roller Coaster Tycoon. Je veux savoir quelle est la meilleure façon de structurer mes objets univers afin de maximiser les performances.

Disons que j'ai 5000 personnes dans mon jeu que je pourrais:

Créez un objet et stockez-les dans un tableau comme celui-ci;

class person() {
    this.x = 0;
    this.y = 0;
    this.thirst = 15;
    this.hunger = 15;
    // etc.. add methods:
    public findPath(int destX, int destY) {
    // and so on
    }

    people = new person[5000];

for (int = 0; i < 5000; i++) {
    people[i] = new person;
    }

Ou devrais-je faire un objet de personnes qui contient de nombreux tableaux d'octets représentant des attributs de personnes comme ceci:

class people() {
    this.hunger = new byte[5000]
    this.thirst = new byte[5000]

    getThirst(int i) {
        return this.thirst[i]
        }

 // and so on....

Ou suis-je totalement hors de propos?

ali_goes_oosh
la source
Question assez intéressante, d'autant plus qu'en 2013, plus d'une douzaine d'années après la sortie du RCT, l'idée d'avoir 5000 PNJ visibles et indépendants dans un monde semblerait tout à fait impossible (malgré les progrès technologiques)
Katana314

Réponses:

15

La terminologie courante est "structure de tableaux" (SOA) et "tableau de structures" (AOS) qui viennent de C et sont le plus souvent considérés en termes de travail SIMD.

En règle générale, l'approche AOS est plus rapide, si elle est utilisée de manière appropriée, mais SOA a tendance à être plus facile à travailler (et donc à optimiser pour la qualité la plus importante - le temps de développement).

SOA, en particulier en Java, signifie que vos données peuvent rester serrées en mémoire. Vous pouvez parcourir les propriétés et vous attendre à ce que le cache du processeur et autres restent satisfaits. Avec AOS, en particulier en Java, chaque objet finit par être alloué "quelque part" en mémoire. Itérer sur des objets peut potentiellement détruire votre cache CPU assez fortement.

En fin de compte, je choisirais l'approche que vous trouvez la plus facile à utiliser. Votre temps de développement est beaucoup plus précieux que si votre jeu prend en charge des PC de 10 ans ou seulement des PC de 9 ans (il est très peu probable que vous fassiez quoi que ce soit qui nécessite le dernier matériel).

Sean Middleditch
la source
1
Dans votre troisième paragraphe, voulez-vous parler de l'AOS deux fois? Les commentaires semblent contradictoires ...
ali_goes_oosh
Désolé, corrigé.
Sean Middleditch
4

Il n'y a aucune raison pour que vous ne puissiez pas avoir les deux, en utilisant le modèle Facade pour traduire d'une interface à l'autre représentation sous-jacente. Par exemple, en utilisant les termes SOA / AOS de Sean:

Façade SOA

class PeopleFacade {
    Person persons[5000];
    getThirst(int i) { return persons[i].thirst; }
}

Façade AOS

class People { int thirsts[5000]; } people;
class PersonFacade {
    int i;
    getThirst() { return people.thirsts[i]; }
}

De cette façon, vous pouvez choisir librement entre un formulaire que vous êtes à l'aise d'utiliser , en tant qu'interface développeur, ou tout ce qui est le mieux en tant qu'implémentation pour une raison quelconque, y compris des raisons d'efficacité / de cache.

Un autre avantage de la façade est qu'elle conduit très naturellement au motif Flyweight , où vous utilisez une interface pour représenter beaucoup plus de personnes que ce qui est réellement en mémoire. Par exemple, vous avez peut-être des clients robotiques qui n'ont jamais soif; alors vous pouvez mettre ce cas spécial dans votre PersonFacade, et les utilisateurs de cette interface n'auront jamais à connaître les robots:

class People { int nonRobotThirsts[1000]; } people;
class PersonFacade {
    int i;
    bool isRobot;
    getThirst() {
        if (isRobot)
            return 0;
        else
            return people.nonRobotThirsts[i];
    }
}

... ou en utilisant une approche plus OO, vous auriez une Robotclasse distincte qui agit exactement comme une Personexception pour getThirst().

congusbongus
la source
-1

Fabriquez des objets et stockez-les dans un tableau! Faire des tableaux pour la faim et la soif peut économiser un peu d'espace et fonctionner plus rapidement dans certaines situations simples, mais ce n'est pas de la POO. Java et OOP feront beaucoup pour vous si vous leur donnez une chance. Pour un jeu vraiment simple, votre deuxième exemple pourrait bien fonctionner, mais même alors, vous devriez pratiquer vos compétences OO. Votre première approche fonctionnera bien pour vous, peu importe l'ampleur, la complexité et la pilosité de votre programme.

Pensez à toutes les fois où il sera utile de récupérer un Personobjet à partir d'une requête. Qui a envoyé ce message? par exemple. Beaucoup de méthodes que vous écrivez voudront savoir à qui elles ont affaire. Et vous aurez beaucoup de méthodes qui s'intégreront bien dans une Personclasse appropriée . Si Personest statique ou singleton, où mettez-vous des méthodes qui agissent sur des personnes individuelles?

Si jamais vous faites du multithreading - et avec 5000 utilisateurs vous pourriez être poussé dedans - vous trouverez l'instance parent pour chaque utilisateur beaucoup plus pratique.

(Et ce groupe de personnes: respectez-le pour l'instant, mais à un moment donné, vous voudrez d'autres périphériques de stockage. Une carte en quelque sorte afin que vous puissiez trouver des personnes par nom. Et peut-être plusieurs listes avec des clés différentes, et probablement des grappes de répertorie chacune suffisamment courte pour être des tableaux ou des listes liées.)

RalphChapin
la source