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