Co to jest projektowanie zorientowane na dane?


156

Czytałem ten artykuł , a ten facet mówi dalej o tym, jak każdy może odnieść wiele korzyści z połączenia projektowania zorientowanego na dane z OOP. Nie pokazuje jednak żadnych próbek kodu.

Przeszukałem to w Google i nie mogłem znaleźć żadnych prawdziwych informacji o tym, co to jest, nie mówiąc już o próbkach kodu. Czy ktoś zna ten termin i może podać przykład? Czy to może inne słowo na coś innego?


7
Ten artykuł w Game Developer jest teraz dostępny w czytelnej formie w formie bloga: gamesfromwithin.com/data-oriented-design
Edmundito

58
Czy kiedykolwiek wygooglowaliście coś w Google, znaleźliście ładne, ukierunkowane pytanie SO, a potem zdaliście sobie sprawę, że to wy zadaliście je lata temu?
ryeguy


14
@ryeguy, miałem pytanie, wyszukałem je w Google, znalazłem fajne pytanie SO, a potem zdałem sobie sprawę, że odpowiedziałem na nie lata temu.
Michael Deardeuff

4
Wyszukałem coś w Google i znalazłem fajne pytanie SO i zgadnij co? To nie ja ani nie pytałem, ani nie odpowiadałem :)
Nadjib Mami

Odpowiedzi:


287

Przede wszystkim nie myl tego z projektowaniem opartym na danych.

W moim rozumieniu Projektowanie zorientowane na dane polega na organizowaniu danych w celu wydajnego przetwarzania. Zwłaszcza jeśli chodzi o chybienia w pamięci podręcznej itp. Z drugiej strony, projektowanie oparte na danych polega na umożliwieniu danych kontrolowania wielu zachowań programów (bardzo dobrze opisane w odpowiedzi Andrew Keitha ).

Załóżmy, że w aplikacji znajdują się obiekty piłek z właściwościami takimi jak kolor, promień, sprężystość, położenie itp.

Podejście zorientowane obiektowo

W OOP opisałbyś kule w ten sposób:

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

  void draw();
};

Następnie stworzyłbyś kolekcję piłek w ten sposób:

vector<Ball> balls;

Podejście zorientowane na dane

Jednak w projektowaniu zorientowanym na dane jest bardziej prawdopodobne, że napiszesz kod w następujący sposób:

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

  void draw();
};

Jak widać, nie ma już pojedynczej jednostki reprezentującej jedną piłkę. Obiekty Ball istnieją tylko niejawnie.

Może to mieć wiele zalet, jeśli chodzi o wydajność. Zwykle chcemy wykonywać operacje na wielu piłkach jednocześnie. Sprzęt zwykle potrzebuje dużych ciągłych porcji pamięci, aby działać wydajnie.

Po drugie, możesz wykonywać operacje, które wpływają tylko na część właściwości piłki. Np. Jeśli na różne sposoby łączysz kolory wszystkich kulek, chcesz, aby twoja pamięć podręczna zawierała tylko informacje o kolorze. Jednak gdy wszystkie właściwości piłki są przechowywane w jednej jednostce, uwzględnisz również wszystkie inne właściwości piłki. Nawet jeśli ich nie potrzebujesz.

Przykład użycia pamięci podręcznej

Powiedzmy, że każda kula zajmuje 64 bajty, a punkt zajmuje 4 bajty. Również miejsce na pamięć podręczną zajmuje, powiedzmy, 64 bajty. Jeśli chcę zaktualizować pozycję 10 piłek, muszę wciągnąć 10 * 64 = 640 bajtów pamięci do pamięci podręcznej i uzyskać 10 braków pamięci podręcznej. Jeśli jednak mogę obrobić pozycje kulek jako oddzielne jednostki, zajmie to tylko 4 * 10 = 40 bajtów. To pasuje do jednego pobrania z pamięci podręcznej. W ten sposób otrzymujemy tylko 1 brak pamięci podręcznej, aby zaktualizować wszystkie 10 piłek. Te liczby są arbitralne - zakładam, że blok pamięci podręcznej jest większy.

Ale ilustruje, jak układ pamięci może mieć poważny wpływ na trafienia w pamięci podręcznej, a tym samym na wydajność. Będzie to rosło tylko w miarę zwiększania się różnicy między szybkością procesora i pamięci RAM.

Jak rozplanować pamięć

W moim przykładzie z piłką bardzo uprościłem problem, ponieważ zwykle w przypadku każdej normalnej aplikacji prawdopodobnie będziesz mieć dostęp do wielu zmiennych jednocześnie. Np. Pozycja i promień będą prawdopodobnie często używane razem. Wtedy twoja struktura powinna być:

class Body {
  Point  position;
  double radius;
};

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

  void draw();
};

Powód, dla którego należy to zrobić, jest taki, że jeśli dane używane razem są umieszczone w oddzielnych tablicach, istnieje ryzyko, że będą konkurować o te same gniazda w pamięci podręcznej. W ten sposób ładowanie jednego spowoduje wyrzucenie drugiego.

Zatem w porównaniu z programowaniem zorientowanym obiektowo, klasy, które ostatecznie tworzysz, nie są powiązane z bytami w twoim mentalnym modelu problemu. Ponieważ dane są grupowane na podstawie wykorzystania danych, nie zawsze będziesz mieć rozsądne nazwy, które nadasz swoim klasom w projektowaniu zorientowanym na dane.

Relacja z relacyjnymi bazami danych

Sposób myślenia dotyczący projektowania zorientowanego na dane jest bardzo podobny do tego, jak myślisz o relacyjnych bazach danych. Optymalizacja relacyjnej bazy danych może również obejmować bardziej wydajne wykorzystanie pamięci podręcznej, chociaż w tym przypadku pamięć podręczna nie jest pamięcią podręczną procesora, ale stronami w pamięci. Dobry projektant bazy danych prawdopodobnie podzieli rzadko używane dane na oddzielną tabelę, zamiast tworzyć tabelę z ogromną liczbą kolumn, gdy tylko kilka z nich jest kiedykolwiek używanych. Może również zdecydować się na denormalizację niektórych tabel, aby nie trzeba było uzyskiwać dostępu do danych z wielu lokalizacji na dysku. Podobnie jak w przypadku projektowania zorientowanego na dane, tych wyborów dokonuje się, patrząc na wzorce dostępu do danych i gdzie jest wąskie gardło wydajności.


4
Dzięki za to, bardzo dobrze to wyjaśniłeś.
ryeguy

4
dobrze powiedziane; Mam tylko jedno pytanie. Powiedzmy, że mamy strukturę struct balls {vector<vec3> pos; vector<vec3> velocity;}, nie aktualizowanie pozycji każdej piłki w rzeczywistości nie powodowałoby wyrzucenia pamięci podręcznej, ponieważ poruszałbyś się tam iz powrotem między wektorem prędkości a wektorem pozycji (tak, nowoczesne maszyny i linie pamięci podręcznej i tak dalej, to jest też tylko ilustracja)?
falstro,

14
Może. Pamiętaj jednak, że cała tablica pozycji nie będzie pobierana naraz. Tylko jedna linia pamięci podręcznej i możliwe wstępne pobieranie. Podobnie z prędkością. Tak więc, aby zdemaskowały się nawzajem, każdy odpowiedni fragment pozycji i wektor musi zostać zmapowany do tej samej linii pamięci. To oczywiście może się zdarzyć, dlatego zaleca się umieszczenie zmiennych, które są używane razem w strukturze. Czyli np. Prędkość i pos byłyby w jednym wektorze, a kolor w innym wektorze.
Erik Engheim

1
@roe Powinieneś zgrupować razem właściwości, do których uzyskuje się dostęp. Między właściwościami nie powinno być żadnych zależności. Więc ta struktura byłaby lepsza struct balls { vector<color> colors; vector<body> bodies; /* contains position and velocity */ }.
danijar

2
@danijar Zaktualizowałem wyjaśnienie twoimi sugestiami. Mógłbym powiedzieć o tym dużo więcej, ale to naprawdę zmieni się w artykuł.
Erik Engheim

18

Mike Acton wygłosił niedawno publiczną prelekcję na temat projektowania zorientowanego na dane :

Moje podstawowe podsumowanie byłoby następujące: jeśli chcesz wydajności, pomyśl o przepływie danych, znajdź warstwę pamięci, która najprawdopodobniej będzie ci przeszkadzać, i zoptymalizuj ją mocno. Mike koncentruje się na chybieniach pamięci podręcznej L2, ponieważ pracuje w czasie rzeczywistym, ale wyobrażam sobie, że to samo dotyczy baz danych (odczyty dysków), a nawet sieci WWW (żądania HTTP). Myślę, że to przydatny sposób programowania systemów.

Zauważ, że nie zwalnia cię to od myślenia o algorytmach i złożoności czasowej, po prostu skupia twoją uwagę na znalezieniu najdroższego typu operacji, do którego musisz następnie skierować swoje szalone umiejętności CS.


14

Chcę tylko zaznaczyć, że Noel mówi konkretnie o niektórych konkretnych potrzebach, z jakimi mamy do czynienia podczas tworzenia gier. Przypuszczam, że inne sektory, które wykonują symulację miękką w czasie rzeczywistym, skorzystałyby na tym, ale jest mało prawdopodobne, aby była to technika, która wykaże zauważalną poprawę w ogólnych zastosowaniach biznesowych. Ta konfiguracja ma na celu zapewnienie, że każdy ostatni kawałek wydajności zostanie wyciśnięty z podstawowego sprzętu.


Zgoda. Inne obszary, w których projektowanie zorientowane na dane jest istotne, to: sprzęt i oprogramowanie sprzętowe dla urządzeń o dużej przepustowości (np. Sieć lub pamięć masowa); obliczenia naukowe na dużą skalę (np. symulacja pogody, fałdowanie białek), przetwarzanie sygnałów (np. audio, obraz, wideo), kompresja danych. Te zaliczają się do „Informatyki i inżynierii”, która jest czasami oferowana jako odrębny kierunek od bardziej typowej informatyki.
rwong,

-3

Projekt zorientowany na dane to projekt, w którym logika aplikacji składa się ze zbiorów danych zamiast algorytmów proceduralnych. Na przykład

podejście proceduralne.

int animation; // this value is the animation index

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

podejście do projektowania danych

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

Takie projekty danych promują wykorzystanie danych do tworzenia logiki aplikacji. Jest łatwiejszy w zarządzaniu, zwłaszcza w grach wideo, które mogą mieć tysiące ścieżek logicznych opartych na animacji lub innym czynniku.


14
To faktycznie nie jest poprawne. Mylisz projektowanie zorientowane na dane z projektowaniem opartym na danych. Robiłem to samo, dopóki nie przeczytałem artykułu Noela i nie zdałem sobie sprawy, że mówi o czymś zupełnie innym.
Erik Engheim,

12
Ponadto Indice nie jest słowem. Istnieją „indeksy” i „indeksy”, a niektórzy nawet akceptują „indeksy”, ale „indeks” nigdy nie jest poprawny.
Baxissimo
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.