209 votes

Qu'est-ce que la conception orientée données ?

Je lisais cet article et ce type continue en expliquant comment tout le monde peut bénéficier d'un mélange de conception orientée données et de POO. Il ne montre cependant aucun exemple de code.

J'ai fait une recherche sur Google et je n'ai trouvé aucune information réelle sur ce que c'est, et encore moins des exemples de code. Quelqu'un connaît-il ce terme et peut-il fournir un exemple ? S'agit-il d'un terme différent pour désigner quelque chose d'autre ?

8 votes

Cet article paru dans Game developer est maintenant disponible sous forme de blog, facile à lire : gamesfromwithin.com/data-oriented-design

1 votes

Voici un Agrégat de contenu DOD sur le web

0 votes

De nombreux autres liens sont également disponibles : github.com/dbartolini/data-oriented-design

375voto

Adam Smith Points 2167

Tout d'abord, ne confondez pas cela avec la conception axée sur les données.

D'après ce que je comprends de la conception orientée données, il s'agit d'organiser vos données pour un traitement efficace. Surtout en ce qui concerne les pertes de cache, etc. La conception pilotée par les données, quant à elle, consiste à laisser les données contrôler une grande partie du comportement de votre programme (très bien décrit par Réponse d'Andrew Keith ).

Disons que vous avez des objets de type balle dans votre application avec des propriétés telles que la couleur, le rayon, le rebond, la position, etc.

Approche orientée objet

En OOP, vous décririez les balles comme ceci :

class Ball {
    Point  position;
    Color  color;
    double radius;

    void draw();
};

Et ensuite, vous créez une collection de balles comme celle-ci :

vector<Ball> balls;

Approche axée sur les données

En revanche, dans le cadre d'une conception orientée données, il est plus probable que vous écriviez le code comme ceci :

class Balls {
    vector<Point>  position;
    vector<Color>  color;
    vector<double> radius;

    void draw();
};

Comme vous pouvez le constater, il n'y a plus d'unité unique représentant une seule boule. Les objets Boule n'existent qu'implicitement.

Cela peut avoir de nombreux avantages, en termes de performances. En général, nous voulons effectuer des opérations sur de nombreuses billes en même temps. Le matériel a généralement besoin de gros blocs de mémoire contigus pour fonctionner efficacement.

Deuxièmement, vous pouvez effectuer des opérations qui n'affectent qu'une partie des propriétés d'une balle. Par exemple, si vous combinez les couleurs de toutes les balles de différentes manières, vous voulez que votre cache ne contienne que des informations sur les couleurs. Cependant, lorsque toutes les propriétés d'une balle sont stockées dans une unité, toutes les autres propriétés de la balle sont également stockées. Même si vous n'en avez pas besoin.

Exemple d'utilisation du cache

Disons que chaque balle occupe 64 octets et qu'un point occupe 4 octets. Un emplacement de cache prend, disons, 64 octets également. Si je veux mettre à jour la position de 10 boules, je dois faire entrer 10 x 64 = 640 octets de mémoire dans le cache et obtenir 10 manques de cache. Par contre, si je peux travailler les positions des billes comme des unités séparées, cela ne prendra que 4 x 10 = 40 octets. Cela correspond à un seul accès au cache. Ainsi, nous n'aurons qu'un seul échec de cache pour mettre à jour les 10 boules. Ces chiffres sont arbitraires - je suppose qu'un bloc de cache est plus grand.

Mais il illustre comment la disposition de la mémoire peut avoir un effet important sur les occurrences du cache et donc sur les performances. Cet aspect ne fera que gagner en importance à mesure que la différence entre la vitesse du CPU et celle de la RAM s'accentuera.

Comment aménager la mémoire

Dans mon exemple de balle, j'ai beaucoup simplifié le problème, car, dans toute application normale, vous accéderez probablement à plusieurs variables ensemble. Par exemple, la position et le rayon seront probablement utilisés ensemble fréquemment. Alors votre structure devrait être :

class Body {
    Point  position;
    double radius;
};

class Balls {
    vector<Body>  bodies;
    vector<Color>  color;

    void draw();
};

La raison pour laquelle vous devez procéder ainsi est que si des données utilisées ensemble sont placées dans des tableaux distincts, il y a un risque qu'elles se disputent les mêmes emplacements dans le cache. Ainsi, le chargement de l'une d'entre elles rejettera l'autre.

Ainsi, par rapport à la programmation orientée objet, les classes que vous finissez par créer ne sont pas liées aux entités de votre modèle mental du problème. Les données étant regroupées en fonction de leur utilisation, vous n'aurez pas toujours de noms judicieux à donner à vos classes dans la conception orientée données.

Relation avec les bases de données relationnelles

Le raisonnement qui sous-tend la conception orientée données est très similaire à celui qui sous-tend les bases de données relationnelles. L'optimisation d'une base de données relationnelle peut également impliquer une utilisation plus efficace du cache, bien que dans ce cas, le cache ne soit pas celui du processeur mais des pages en mémoire. Un bon concepteur de base de données est également susceptible de répartir les données rarement consultées dans une table séparée plutôt que de créer une table avec un grand nombre de colonnes dont seules quelques-unes sont utilisées. Il peut également choisir de dénormaliser certaines tables afin que les données ne soient pas accessibles à partir de plusieurs emplacements sur le disque. Comme dans le cas de la conception orientée données, ces choix sont faits en examinant les modèles d'accès aux données et les goulots d'étranglement en matière de performances.

7 votes

Merci pour cet article, vous l'avez très bien expliqué.

4 votes

Bien dit ; je n'ai cependant qu'une seule question. Disons que nous avons une structure struct balls {vector<vec3> pos; vector<vec3> velocity;} La mise à jour de la position de chaque boule ne va-t-elle pas en fait détruire le cache puisque vous allez et venez entre le vecteur vitesse et le vecteur position (oui, les machines modernes et les lignes de cache et tout ça, c'est aussi juste une illustration) ?

16 votes

C'est possible. Mais rappelez-vous que l'ensemble de la matrice pos n'est pas tiré à la fois. Juste une ligne de cache, et peut-être un peu de prefetching. De même pour la vélocité. Donc pour qu'ils puissent se détruire mutuellement, chaque morceau correspondant de pos et de vector doit correspondre à la même ligne de cache. Cela peut bien sûr arriver, c'est pourquoi la recommandation est de mettre les variables qui sont utilisées ensemble dans une structure. Ainsi, par exemple, la vitesse et la position seraient dans un vecteur, tandis que la couleur serait dans un autre vecteur.

25voto

Alexei Averchenko Points 383

Mike Acton a donné une conférence publique sur Conception orientée données récemment :

Pour résumer, je dirais que si vous voulez des performances, pensez au flux de données, trouvez la couche de stockage qui est la plus susceptible de vous embêter et optimisez-la. dur. Mike se concentre sur les ratés du cache L2, parce qu'il fait du temps réel, mais j'imagine que la même chose s'applique aux bases de données (lectures de disque) et même au Web (requêtes HTTP). C'est une façon utile de faire de la programmation système, je pense.

Notez que cela ne vous dispense pas de réfléchir aux algorithmes et à la complexité temporelle, mais que vous vous concentrez sur la détermination du type d'opération le plus coûteux, que vous devez ensuite cibler grâce à vos compétences en informatique.

0 votes

D'accord, probablement pas des requêtes HTTP :)

16voto

bill c Points 61

Je tiens simplement à souligner que Noel parle spécifiquement de certains des besoins spécifiques auxquels nous sommes confrontés dans le développement de jeux. Je suppose que d'autres secteurs qui font de la simulation douce en temps réel pourraient en bénéficier, mais il est peu probable que cette technique apporte une amélioration notable aux applications commerciales générales. Cette configuration permet de s'assurer que le matériel sous-jacent est exploité au maximum de ses performances.

0 votes

Approuvé. Parmi les autres domaines où la conception orientée données est importante, citons : le matériel et les microprogrammes pour les dispositifs à large bande passante (par exemple, les réseaux ou le stockage) ; le calcul scientifique à grande échelle (par exemple, la simulation météorologique, le repliement des protéines), le traitement du signal (par exemple, l'audio, l'image, la vidéo), la compression des données. Ces domaines relèvent de la "science et de l'ingénierie informatiques", qui est parfois proposée comme une spécialisation distincte de l'informatique plus classique.

4voto

Andrew Keith Points 5627

Une conception orientée données est une conception dans laquelle la logique de l'application est construite à partir d'ensembles de données, au lieu d'algorithmes procéduraux. Par exemple

approche procédurale.

int animation; // this value is the animation index

if(animation == 0)
   PerformMoveForward();
else if(animation == 1)
  PerformMoveBack();
.... // etc

approche de la conception des données

typedef struct
{
   int Index;
   void (*Perform)();
}AnimationIndice;

// build my animation dictionary
AnimationIndice AnimationIndices[] = 
  {
      { 0,PerformMoveForward }
      { 1,PerformMoveBack }
  }

// when its time to run, i use my dictionary to find my logic
int animation; // this value is the animation index
AnimationIndices[animation].Perform();

Les conceptions de données de ce type favorisent l'utilisation des données pour construire la logique de l'application. C'est plus facile à gérer, notamment dans les jeux vidéo qui peuvent avoir des milliers de chemins logiques basés sur l'animation ou d'autres facteurs.

14 votes

Ce n'est pas correct. Vous confondez la conception orientée données avec la conception pilotée par les données. J'ai fait la même chose jusqu'à ce que je lise l'article de Noel et que je réalise qu'il parlait de quelque chose d'entièrement différent.

14 votes

De plus, Indice n'est pas un mot. Il y a "index" et "indices" et certains tolèrent même les "index", mais "indice" n'est jamais correct.

0 votes

J'utilise "dex" pour index et j'évite d'utiliser le pluriel dans mon code car les règles pour mettre les choses au pluriel ne sont pas régulières. Et le code devrait être régulier. Si vous devez briser les règles de l'anglais pour rendre le code plus [ uniforme / régulier ] faites-le.

0voto

Bogi Points 580

Si vous voulez tirer parti de l'architecture des processeurs modernes, vous devez disposer vos données en mémoire d'une certaine manière. Les processeurs sont très performants pour traiter des types simples disposés séquentiellement en mémoire. Toute autre disposition a un coût de traitement beaucoup plus élevé.

Dans l'approche orientée objet, on pense toujours à une instance, puis on l'étend à plusieurs instances en regroupant les objets en collections. Mais du point de vue du matériel, cela entraîne un coût supplémentaire.

Dans l'approche orientée données, vous n'avez pas d'"instance" comme dans la programmation orientée objet. Votre instance peut avoir un identifiant, similaire aux données des bases de données relationnelles, mais en dehors de cela, les données liées à votre instance peuvent être réparties sur plusieurs tables (les tables sont implémentées comme des vecteurs), pour permettre un traitement efficace.

Un exemple : imaginez que vous avez la classe Student { int id ; std::string name ; float average ; bool graduated ; }. Dans le cas de la POO, vous placeriez tous vos étudiants dans un seul vecteur.

Dans la conception orientée données, vous vous demanderez d'abord quel type de traitement vous voulez faire subir à ces données. Supposons que vous vouliez calculer une note moyenne pour tous les étudiants qui n'ont pas encore obtenu leur diplôme. Vous allez donc créer une table contenant uniquement les étudiants qui ont obtenu leur diplôme, et les autres qui ne l'ont pas obtenu. Vous ne conserverez pas le nom de l'étudiant dans cette table puisqu'il n'est pas utilisé pour le traitement. Mais vous conserverez un identifiant d'étudiant et une note moyenne dans la table.

Maintenant, pour calculer la note moyenne des étudiants non diplômés, il faudra itérer dans le tableau des non diplômés et effectuer le calcul. Comme les notes moyennes sont voisines en mémoire, votre CPU utilisera SIMD et traitera les données de la manière la plus efficace possible. Puisque nous n'interrogeons pas le bool graduated pour vérifier si l'étudiant a obtenu son diplôme, il n'y a pas de perte de données dans le cache.

Cela semble bien en théorie mais je n'ai jamais fait ce genre de développement sur un projet réel. Si quelqu'un a de l'expérience, veuillez me contacter, j'ai beaucoup de questions.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X