Cos'è la progettazione orientata ai dati?


156

Stavo leggendo questo articolo e questo ragazzo continua a parlare di come tutti possano trarre grandi benefici dal mescolare la progettazione orientata ai dati con OOP. Tuttavia, non mostra alcun esempio di codice.

Ho cercato su Google questo e non sono riuscito a trovare alcuna informazione reale su cosa sia, per non parlare di esempi di codice. Qualcuno ha familiarità con questo termine e può fornire un esempio? È forse una parola diversa per qualcos'altro?


7
Quell'articolo nello sviluppatore di giochi è ora disponibile in facile lettura nel modulo blog: gamesfromwithin.com/data-oriented-design
Edmundito

58
Ragazzi avete mai cercato su Google qualcosa, trovato una bella domanda mirata su SO, e poi avete capito che siete stati voi a chiederlo anni fa?
ryeguy,


14
@ryeguy, ho avuto una domanda, l'ho cercata su Google, ho trovato una bella domanda SO, e poi ho capito che avevo risposto anni fa.
Michael Deardeuff,

4
Ho cercato su Google qualcosa e ho trovato una bella domanda SO e indovinate un po '? Non sono stato né io a chiedere né a rispondere :)
Nadjib Mami,

Risposte:


288

Prima di tutto, non confonderlo con la progettazione basata sui dati.

La mia comprensione del design orientato ai dati è che si tratta di organizzare i dati per un'elaborazione efficiente. Soprattutto per quanto riguarda i mancati cache ecc. Data Driven Design, d'altra parte, consente al controllo dei dati di gran parte del comportamento dei programmi (descritto molto bene dalla risposta di Andrew Keith ).

Supponi di avere oggetti a sfera nella tua applicazione con proprietà come colore, raggio, rimbalzo, posizione ecc.

Approccio orientato agli oggetti

In OOP descriveresti palle come questa:

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

  void draw();
};

E poi creeresti una raccolta di palline come questa:

vector<Ball> balls;

Approccio orientato ai dati

Nella progettazione orientata ai dati, tuttavia, è più probabile che tu scriva il codice in questo modo:

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

  void draw();
};

Come puoi vedere, non esiste più una singola unità che rappresenti più una palla. Gli oggetti palla esistono solo implicitamente.

Questo può avere molti vantaggi, dal punto di vista delle prestazioni. Di solito vogliamo fare operazioni su molte palline contemporaneamente. L'hardware di solito richiede grandi blocchi continui di memoria per funzionare in modo efficiente.

In secondo luogo, è possibile eseguire operazioni che riguardano solo una parte delle proprietà delle sfere. Ad esempio, se si combinano i colori di tutte le sfere in vari modi, si desidera che la cache contenga solo informazioni sul colore. Tuttavia, quando tutte le proprietà della palla sono memorizzate in un'unità, tirerai anche tutte le altre proprietà di una palla. Anche se non ne hai bisogno.

Esempio di utilizzo della cache

Supponiamo che ogni palla occupi 64 byte e che un Punto richieda 4 byte. Uno slot della cache richiede, diciamo, anche 64 byte. Se voglio aggiornare la posizione di 10 palline, devo inserire 10 * 64 = 640 byte di memoria nella cache e ottenere 10 mancate cache. Se comunque riesco a lavorare le posizioni delle sfere come unità separate, ci vorranno solo 4 * 10 = 40 byte. Si adatta a un recupero della cache. Quindi abbiamo solo 1 cache mancata per aggiornare tutte e 10 le palline. Questi numeri sono arbitrari - presumo che un blocco di cache sia più grande.

Ma illustra come il layout della memoria può avere un grave effetto sugli hit della cache e quindi sulle prestazioni. Ciò aumenterà di importanza solo con l'aumentare della differenza tra la velocità della CPU e quella della RAM.

Come impaginare la memoria

Nel mio esempio palla ho semplificato molto il problema, perché di solito per qualsiasi normale app si accederà probabilmente a più variabili insieme. Ad esempio, la posizione e il raggio verranno probabilmente usati insieme frequentemente. Quindi la tua struttura dovrebbe essere:

class Body {
  Point  position;
  double radius;
};

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

  void draw();
};

La ragione per cui dovresti fare questo è che se i dati usati insieme sono collocati in array separati, c'è il rischio che competano per gli stessi slot nella cache. In questo modo, caricando l'uno verrà eliminato l'altro.

Quindi, rispetto alla programmazione orientata agli oggetti, le classi che finisci per fare non sono correlate alle entità nel tuo modello mentale del problema. Poiché i dati sono raggruppati in base all'utilizzo dei dati, non avrai sempre nomi ragionevoli da dare alle tue classi in Design orientato ai dati.

Relazione con database relazionali

Il pensiero alla base del design orientato ai dati è molto simile a come si pensa ai database relazionali. L'ottimizzazione di un database relazionale può anche comportare l'utilizzo della cache in modo più efficiente, sebbene in questo caso la cache non sia cache della CPU ma pagine in memoria. Un buon progettista di database probabilmente dividerà i dati a cui si accede raramente in una tabella separata piuttosto che creare una tabella con un numero enorme di colonne dove solo alcune delle colonne sono mai state utilizzate. Potrebbe anche scegliere di denormalizzare alcune delle tabelle in modo che non sia necessario accedere ai dati da più posizioni sul disco. Proprio come con il design orientato ai dati, queste scelte vengono prese osservando quali sono i modelli di accesso ai dati e dove si trova il collo di bottiglia delle prestazioni.


4
Grazie per questo, l'hai spiegato molto bene.
ryeguy,

4
ben detto; Ho solo una domanda però. Diciamo che abbiamo una struttura struct balls {vector<vec3> pos; vector<vec3> velocity;}, che non aggiornerebbe la posizione di ogni pallina in realtà distruggerebbe la cache poiché ti sposteresti avanti e indietro tra il vettore di velocità e il vettore di posizione (sì macchine moderne e linee di cache e tutto il resto, questo è anche solo un'illustrazione)?
falstro,

14
Potrebbe. Ma ricorda che l'intero pos array non verrà richiamato alla volta. Solo una riga della cache e possibile alcuni prefetching. Allo stesso modo con la velocità. Quindi per loro di cestinarsi a vicenda ogni pezzo corrispondente di pos e vector deve mappare sulla stessa cacheline. Ciò può ovviamente accadere, motivo per cui la raccomandazione è di mettere insieme le variabili che sono usate insieme in una struttura. Quindi, ad esempio, velocità e pos sarebbero in un vettore mentre il colore sarebbe in un altro vettore.
Erik Engheim,

1
@roe È necessario raggruppare le proprietà a cui si accede insieme. Tra le proprietà non dovrebbero esserci dipendenze. Quindi questa struttura sarebbe migliore struct balls { vector<color> colors; vector<body> bodies; /* contains position and velocity */ }.
danijar,

2
@danijar Ho aggiornato la spiegazione con i tuoi suggerimenti. Avrei potuto dire molto di più a riguardo, ma questo si trasformerà davvero in un articolo.
Erik Engheim,

18

Di recente Mike Acton ha tenuto un discorso pubblico sul design orientato ai dati :

Il mio riassunto di base sarebbe: se vuoi prestazioni, quindi pensa al flusso di dati, trova il livello di archiviazione che è più probabile che ti rovini e ottimizza per questo . Mike si sta concentrando sui fallimenti della cache L2, perché sta facendo in tempo reale, ma immagino che la stessa cosa si applichi ai database (letture del disco) e persino al Web (richieste HTTP). È un modo utile per programmare i sistemi, credo.

Nota che non ti assolve dal pensare agli algoritmi e alla complessità del tempo, focalizza solo la tua attenzione a capire il tipo di operazione più costoso che devi quindi colpire con le tue pazze abilità CS.


14

Voglio solo sottolineare che Noel sta parlando specificamente di alcune delle esigenze specifiche che dobbiamo affrontare nello sviluppo del gioco. Suppongo che altri settori che stanno eseguendo una simulazione soft in tempo reale ne trarrebbero beneficio, ma è improbabile che si tratti di una tecnica che mostrerà un notevole miglioramento delle applicazioni aziendali generali. Questa impostazione serve a garantire che ogni ultimo bit di prestazioni venga eliminato dall'hardware sottostante.


Concordato. Alcune altre aree in cui la progettazione orientata ai dati è significativa sono: hardware e firmware per dispositivi ad alta larghezza di banda (ad es. Rete o archiviazione); elaborazione scientifica su larga scala (ad es. simulazione del tempo, ripiegamento delle proteine), elaborazione del segnale (ad es. audio, immagine, video), compressione dei dati. Questi rientrano nella "Scienza e ingegneria computazionale" che a volte viene offerta come maggiore separato dalla più tipica informatica.
rwong

-3

Una progettazione orientata ai dati è una progettazione in cui la logica dell'applicazione è costruita su set di dati, anziché su algoritmi procedurali. Per esempio

approccio procedurale.

int animation; // this value is the animation index

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

approccio alla progettazione dei dati

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();

Progettazioni di dati come questa promuovono l'uso dei dati per costruire la logica dell'applicazione. È più facile da gestire soprattutto nei videogiochi che potrebbero avere migliaia di percorsi logici basati su animazioni o altri fattori.


14
Questo in realtà non è corretto. Stai confondendo la progettazione orientata ai dati con la progettazione guidata dai dati. Ho fatto la stessa cosa fino a quando ho letto l'articolo di Noel e mi sono reso conto che stava parlando di qualcosa di completamente diverso.
Erik Engheim,

12
Inoltre, Indice non è una parola. Ci sono "indice" e "indici" e alcuni addirittura perdonano "indici", ma "indice" non è mai giusto.
Baxissimo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.