What is data oriented design?

拈花ヽ惹草 提交于 2019-11-26 19:16:54
Erik Engheim

First of all don't confuse this with data driven design.

My understanding of Data Oriented Design is that it is about organizing your data for efficient processing. Especially with respect to cache misses etc. Data Driven Design on the other hand is about letting data control a lot of your programs behavior (described very well by Andrew Keith's answer).

Say you have ball objects in your application with properties such as color, radius, bounciness, position etc.

Object Oriented Approach

In OOP you would describe you balls like this:

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

  void draw();
};

And then you would create a collection of balls like this:

vector<Ball> balls;

Data Oriented Approach

In Data Oriented Design however you are more likely to write the code like this:

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

  void draw();
};

As you can see there is no single unit representing one Ball anymore. Ball objects only exist implicitly.

This can have many advantages performance wise. Usually we want to do operations on many balls at the same time. Hardware usually wants large continuous chunks of memory to operate efficiently.

Secondly you might do operations that affects only part of a balls properties. E.g. if you combine the colors of all the balls in various ways, then you want your cache to only contain color information. However when all ball properties are stored in one unit you will pull in all the other properties of a ball as well. Even though you don't need them.

Cache Usage Example

Say a ball each ball takes up 64 bytes and a Point takes 4 bytes. A cache slot takes say 64 bytes as well. If I want to update the position of 10 balls I have to pull in 10*64 = 640 bytes of memory into cache and get 10 cache misses. If however I can work the positions of the balls as separate units, that will only take 4*10 = 40 bytes. That fits in one cache fetch. Thus we only get 1 cache miss to update all the 10 balls. These numbers are arbitrary I assume a cache block is bigger.

But it illustrates how memory layout can have severe effect cache hits and thus performance. This will only increase in importance as the difference between CPU and RAM speed widens.

How to layout the memory

In my ball example I simplified the issue a lot, because usually for any normal app you will likely access multiple variables together. E.g. position and radius will probably be used together frequently. Then your structure should be:

class Body {
  Point  position;
  double radius;
};

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

  void draw();
};

The reason you should do this is that if data used together are placed in separate arrays, there is a risk that they will compete for the same slots in the cache. Thus loading one will throw out the other.

So compared to Object Oriented programming the classes you end up making are not related to the entities in your mental model of the problem. Since data is lumped together based on data usage, you won't always have sensible names to give your classes in Data Oriented Design.

Relation to relational databases

The thinking behind Data Oriented Design is very similar to how you think about relational databases. Optimizing a relational database can also involve using the cache more efficient, although in this case, the cache is not CPU cache put pages in memory. A good data base designer will also likely split out infrequently accessed data into a separate table rather than creating a table with huge number of columns were only a few of the columns are ever used. He might also choose to denormalize some of the tables so that data don't have to be accessed from multiple locations on disk. Just like with Data Oriented Design these choices are made by looking at what the data access patterns are and where the performance bottleneck is.

Mike Acton gave a public talk about Data oriented design recently:

My basic summary of it would be: if you want performance, then think about data flow, find the storage layer that is most likely to screw with you and optimize for it hard. Mike is focusing on L2 cache misses, because he's doing realtime, but I imagine the same thing applies to databases (disk reads) and even the Web (HTTP requests). It's a useful way of doing systems programming, I think.

Note that it doesn't absolve you from thinking about algorithms and time complexity, it just focuses your attention at figuring out the most expensive operation type that you then must target with your mad CS skills.

I just want to point out that Noel is talking specifically about some of the specific needs we face in game development. I suppose other sectors that are doing real-time soft simulation would benefit from this, but it is unlikely to be a technique that will show noticeable improvement to general business applications. This set up is for ensuring that every last bit of performance is squeezed out of the underlying hardware.

A data oriented design is a design in which the logic of the application is built up of data sets, instead of procedural algorithms. For example

procedural approach.

int animation; // this value is the animation index

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

data design approach

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

Data designs like this promote the usage of data to build the logic of the application. Its easier to manage especially in video games which might have thousands of logic paths based on animation or some other factor.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!