Cos’è la progettazione orientata ai dati?

Stavo leggendo questo articolo , e questo ragazzo continua a parlare di come tutti possano trarre grandi benefici dal mixaggio del design orientato ai dati con OOP. Tuttavia, non mostra alcun esempio di codice.

Ho cercato su Google 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?

Innanzitutto non confondere questo con la progettazione guidata dai dati.

La mia comprensione di Data Oriented Design è che si tratta di organizzare i dati per un’elaborazione efficiente. Soprattutto per quanto riguarda le mancanze della cache ecc. Data Driven Design d’altra parte si occupa di permettere al controllo dei dati un sacco di comportamenti del tuo programma (descritto molto bene dalla risposta di Andrew Keith ).

Supponiamo che tu abbia oggetti palla nella tua applicazione con proprietà come colore, raggio, punto di rimbalzo, posizione ecc.

Approccio orientato agli oggetti

In OOP ti descriveresti palle come questa:

class Ball { Point position; Color color; double radius; void draw(); }; 

E poi dovresti creare una collezione di palle come questa:

 vector balls; 

Approccio orientato ai dati

In Data Oriented Design tuttavia è più probabile scrivere il codice in questo modo:

 class Balls { vector position; vector color; vector radius; void draw(); }; 

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

Questo può avere molti vantaggi in termini di prestazioni. Di solito vogliamo fare operazioni su molte palle allo stesso tempo. L’hardware di solito richiede grandi quantità continue di memoria per funzionare in modo efficiente.

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

Esempio di utilizzo della cache

Dire una palla ogni palla occupa 64 byte e un punto prende 4 byte. Uno slot di cache richiede anche 64 byte. Se voglio aggiornare la posizione di 10 palle, devo inserire 10 * 64 = 640 byte di memoria nella cache e ottenere 10 errori di cache. Se comunque posso lavorare le posizioni delle palle come unità separate, ciò richiederà solo 4 * 10 = 40 byte. Questo si adatta a un recupero della cache. Quindi riceviamo solo 1 cache miss per aggiornare tutte e 10 le palle. Questi numeri sono arbitrari. Suppongo che un blocco di cache sia più grande.

Ma illustra come il layout della memoria possa avere effetti di cache con effetti gravi e quindi prestazioni. Ciò aumenterà di importanza in quanto la differenza tra la CPU e la velocità della RAM aumenta.

Come impaginare la memoria

Nell’esempio della mia pallina ho semplificato molto il problema, perché di solito per qualsiasi app normale probabilmente accederai a più variabili insieme. Ad esempio, la posizione e il raggio saranno probabilmente usati insieme di frequente. Quindi la tua struttura dovrebbe essere:

 class Body { Point position; double radius; }; class Balls { vector bodies; vector color; void draw(); }; 

La ragione per cui dovresti farlo è che se i dati usati insieme sono collocati in array separati, c’è il rischio che competano per gli stessi slot nella cache. Così caricando uno si butta fuori l’altro.

Quindi, rispetto alla programmazione orientata agli oggetti, le classi che si formano non sono correlate alle entity framework del modello mentale del problema. Poiché i dati sono raggruppati in base all’utilizzo dei dati, non sempre avrai nomi ragionevoli per dare lezioni in Data Oriented Design.

Relazione con i database relazionali

Il pensiero alla base di Data Oriented Design è molto simile a come si pensa ai database relazionali. L’ottimizzazione di un database relazionale può anche implicare l’uso della cache più efficiente, sebbene in questo caso la cache non sia la cache della CPU che mette le pagine in memoria. Un buon progettista di base dati probabilmente dividerà i dati con accesso non frequente in una tabella separata invece di creare una tabella con un numero enorme di colonne solo se alcune delle colonne verranno utilizzate. Potrebbe anche scegliere di denormalizzare alcune delle tabelle in modo che i dati non debbano essere accessibili da più posizioni sul disco. Proprio come con Data Oriented Design, queste scelte sono fatte considerando quali sono i pattern di accesso ai dati e dove si trova il collo di bottiglia delle prestazioni.

Voglio solo far notare che Noel sta parlando in modo specifico di alcune delle esigenze specifiche che affrontiamo nello sviluppo del gioco. Suppongo che altri settori che stanno facendo simulazioni soft in tempo reale ne traggano vantaggio, ma è improbabile che sia una tecnica che mostrerà un miglioramento evidente per le applicazioni aziendali generali. Questa configurazione serve a garantire che ogni ultimo bit di prestazioni venga eliminato dall’hardware sottostante.

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

Il mio sumrio di base sarebbe: se vuoi prestazioni, pensa al stream di dati, trova il livello di storage che è più probabile che ti diventi e ottimizzalo duramente. Mike si sta concentrando sulle missioni 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 di programmare i sistemi, credo.

Nota che non ti assolve dal pensare agli algoritmi e alla complessità del tempo, ma focalizza la tua attenzione sulla determinazione del tipo di operazione più costoso che devi quindi indirizzare con le tue folli abilità CS.

Un design orientato ai dati è un progetto in cui la logica dell’applicazione è costituita da set di dati, anziché da 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(); 

I progetti di dati come questo promuovono l’uso dei dati per build la logica dell’applicazione. È più facile da gestire soprattutto nei videogiochi che potrebbero avere migliaia di percorsi logici basati sull’animazione o su altri fattori.