什么是面向数据的设计?


156

我正在阅读本文,而这个家伙继续谈论每个人如何从面向数据的设计与OOP混合中可以从中大大受益。但是,他没有显示任何代码示例。

我在Google上搜索了一下,却找不到任何真正的信息,更不用说任何代码示例了。是否有人熟悉此术语并可以提供示例?这可能是别的词吗?


7
游戏开发人员中的该文章现已以易于阅读的博客形式提供:gamesfromwithin.com/data-directional-design
Edmundito 2011年

58
你们曾经在Google上搜索过一些东西,找到了一个很好的针对性的SO问题,然后才意识到是您几年前问过的吗?
ryeguy

1
这是网上DOD内容汇总
legends2k 2014年

14
@ryeguy,我有一个问题,用谷歌搜索,找到一个不错的问题,然后意识到我几年前回答了。
Michael Deardeuff

4
我用Google搜索了一些东西,发现了一个很好的SO问题,您猜怎么着?不是我既不问谁也不回答:)
Nadjib Mami 2014年

Answers:


287

首先,不要将其与数据驱动设计混淆。

我对面向数据的设计的理解是,它是关于组织数据以进行有效处理的。尤其是关于高速缓存未命中等方面。另一方面,数据驱动设计是关于让数据控制许多程序行为(由Andrew Keith的回答很好地描述了)。

假设您的应用程序中有球对象,这些球对象具有颜色,半径,反弹度,位置等属性。

面向对象的方法

在OOP中,您将这样描述球:

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

  void draw();
};

然后您将创建如下所示的球集合:

vector<Ball> balls;

面向数据的方法

但是,在面向数据的设计中,您更有可能编写如下代码:

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

  void draw();
};

如您所见,不再有单个单位代表一个球。球对象仅隐式存在。

在性能方面,这可以具有许多优点。通常,我们希望同时对多个球进行操作。硬件通常希望连续的大块内存有效运行。

其次,您可能会执行只影响部分球属性的操作。例如,如果您以各种方式组合所有球的颜色,则您希望缓存仅包含颜色信息。但是,当所有球属性都存储在一个单元中时,您还将拉入球的所有其他属性。即使您不需要它们。

缓存使用示例

假设每个球占用64个字节,一个点占用4个字节。缓存插槽也占用64个字节。如果我想更新10个球的位置,则必须将10 * 64 = 640字节的内存拉入缓存并获得10个缓存未命中。但是,如果我可以将球的位置作为单独的单元工作,那将仅占用4 * 10 = 40字节。这适合一次缓存提取。因此,我们只有1个缓存未命中来更新所有10个球。这些数字是任意的-我假设缓存块更大。

但是它说明了内存布局如何严重影响缓存命中率并进而影响性能。随着CPU和RAM速度差异的扩大,这只会增加重要性。

如何布置内存

在我的示例中,我简化了很多问题,因为通常对于任何普通应用程序,您可能会一起访问多个变量。例如,位置和半径可能会经常一起使用。然后,您的结构应为:

class Body {
  Point  position;
  double radius;
};

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

  void draw();
};

您应该这样做的原因是,如果一起使用的数据放置在单独的阵列中,则存在争夺缓存中相同插槽的风险。因此,加载一个将丢弃另一个。

因此,与面向对象的编程相比,最终创建的类与问题的心理模型中的实体无关。由于数据是根据数据使用情况汇总在一起的,因此在“面向数据的设计”中,您不会总是有一个明智的名称来命名您的类。

与关系数据库的关系

面向数据设计的思想与您对关系数据库的思想非常相似。优化关系数据库还可以更有效地使用缓存,尽管在这种情况下,缓存不是CPU缓存而是内存中的页面。一个好的数据库设计人员还可能将不经常访问的数据拆分到一个单独的表中,而不是创建一个包含大量列的表,因为只有少数列曾被使用过。他可能还选择对某些表进行非规范化,以便不必从磁盘上的多个位置访问数据。就像面向数据的设计一样,这些选择是通过查看数据访问模式是什么以及性能瓶颈在哪里来进行的。


4
谢谢你,你解释得很好。
ryeguy 2010年

4
说得好; 我只有一个问题。假设我们有一个结构struct balls {vector<vec3> pos; vector<vec3> velocity;},实际上不会更新每个球的位置,因为您会在速度向量和位置向量之间来回移动(是现代机器和缓存行,而这一切都是也只是一个例子)?
falstro,2010年

14
它可能。但是请记住,整个pos数组不会一次被拉入。仅一个缓存行,并可能进行一些预取。速度也一样。因此,对于它们而言,彼此相对应的pos和vector的相应块必须映射到相同的缓存行。当然可以发生这种情况,这就是为什么建议将将一起使用的变量放在一个结构中的原因。因此,例如,速度和pos将在一个向量中,而颜色将在另一向量中。
埃里克·恩海姆

1
@roe您应该将一起访问的属性分组在一起。在属性之间应该完全没有依赖关系。所以这种结构会更好struct balls { vector<color> colors; vector<body> bodies; /* contains position and velocity */ }
danijar

2
@danijar我用您的建议更新了解释。我本来可以说更多的话,但这实际上只是一篇文章。
Erik Engheim 2014年

18

Mike Acton 最近发表了有关面向数据的设计的公开演讲:

我的基本总结是:如果您想获得性能,那么考虑一下数据流,找到最有可能困扰您的存储层并对其进行优化Mike专注于L2缓存未命中,因为他正在实时进行,但是我想同样的事情也适用于数据库(磁盘读取)甚至Web(HTTP请求)。我认为这是进行系统编程的一种有用方法。

请注意,它并不会使您不必考虑算法和时间复杂性,它只会将您的注意力集中在找出最昂贵的操作类型上,然后您必须用疯狂的CS技能来瞄准该类型。


14

我只想指出,诺埃尔(Noel)专门谈论我们在游戏开发中面临的一些特定需求。我想其他正在进行实时软仿真的部门将从中受益,但是这种技术不太可能对一般业务应用程序显示出明显的改进。这种设置是为了确保性能的每一点都从底层硬件中挤出来。


同意 面向数据设计非常重要的其他领域包括:高带宽设备(例如,网络或存储)的硬件和固件;大规模科学计算(例如天气模拟,蛋白质折叠),信号处理(例如音频,图像,视频),数据压缩。这些都属于“计算科学与工程”的范畴,该课程有时与更典型的计算机科学作为单独的专业提供。
rwong

-3

面向数据的设计是一种设计,其中应用程序的逻辑由数据集而非程序算法构成。例如

程序方法。

int animation; // this value is the animation index

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

数据设计方法

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

像这样的数据设计促进了数据的使用来构建应用程序的逻辑。它更易于管理,尤其是在可能具有基于动画或其他因素的数千条逻辑路径的视频游戏中。


14
这实际上是不正确的。您正在将面向数据的设计与数据驱动的设计混淆。在读了Noel的文章并意识到他在谈论完全不同的事情之前,我做过同样的事情。
Erik Engheim 2010年

12
另外,Indice也不是一个词。有“索引”和“索引”,甚至有宽容的“索引”,但是“索引”永远都不对。
Baxissimo 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.