问题标签 [data-oriented-design]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
0 回答
116 浏览

c# - 如何使用 ECS/DOD 实现第三方插件,例如 IAP、广告网络、社交共享。

我最近开始研究 ECS 以进行游戏开发。直到现在我使用并喜欢 OOP 和代码分离模式,ECS 也这样做,但以另一种方式。我正在考虑在 ECS 中设计数据。对于与游戏相关的东西,我似乎是合乎逻辑的。例如在某些基础构建游戏或任何其他游戏中的位置组件旋转组件价格组件等。但我们还必须集成插件 IAP、分析、社交功能、广告网络,我目前无法理解如何使用 ECS 构建这些插件,只有 OOP 出现在我的脑海中。例如

  • 可以有多种社交功能。在 FB、Twitter 上分享。
  • 为不同的平台实施不同或相同的 adNetowrks。

这些功能可以针对不同平台(IOS、Android、Windows 等)使用不同的插件/SDK/类。

我正在使用 Unity 游戏引擎。

0 投票
0 回答
262 浏览

c++ - 面向数据的碰撞检测

我试图从面向数据的角度进行碰撞检测。一旦检测到碰撞,我想移除敌人,不知道如何使其尽可能优化 CPU,现在我这样做:

像我现在一样立即从向量中删除整数对是否可以,或者我应该以某种方式将所有“检测到”的碰撞存储在另一个向量中并从那里继续?我是新来的以数据为导向的术语思考,任何帮助将不胜感激。

0 投票
1 回答
3604 浏览

rust - 如何在 Rust 中实现面向数据的设计?

背景

在游戏引擎开发中,我们通常使用面向数据的设计来优化内存和计算性能。

我们以粒子系统为例。

在一个粒子系统中,我们有很多粒子,每个粒子可能有几个属性,比如位置、速度等。

C++ 中的典型实现如下所示:

这种实现的一个问题是粒子属性是相互交错的。这种内存布局对缓存不友好,可能不适合 SIMD 计算。

相反,在面向数据的设计中,我们编写以下代码:

现在我们只有一个分配,每个属性都连续驻留在内存中。为了模拟粒子,我们可以编写以下代码:

getAttribute函数将获取特定属性的起始地址。

问题

我想知道如何在 Rust 中实现这一点。

我的想法是首先创建一个名为 的类ParticleSystem,它需要几个ParticleAttributes 来计算总缓冲区大小,然后为缓冲区分配内存。我认为这可以在 Rust 安全代码中完成。

下一步是实现getAttribute函数,它将返回对特定属性起始地址的引用。我在这里需要你的帮助。如何获取带有偏移量的原始地址并将其转换为所需的类型(例如 float*)并将该原始指针包装到 Rust 中的可变引用?

此外,我认为我应该将该原始指针包装到对数组的可变引用,因为我需要使用 SIMD lib 通过该引用加载四个元素。如何使用 Rust 实现这一点?


更新:提供有关属性的更多信息。属性的数量和详细信息在运行时确定。属性的类型可能会有所不同,但我认为我们只需要支持原始类型(f32、f64、ints、...)。

0 投票
1 回答
7140 浏览

caching - 我对 AoS 与 SoA 优势/劣势的理解是否正确?

我最近一直在阅读有关AoS 与 SoA结构设计和面向数据的设计的文章。很难找到有关两者的信息,而且我发现的似乎假设对处理器功能的了解比我拥有的更多。也就是说,我对前一个主题的理解尤其会导致一些我认为我应该能够理解答案的问题。

首先,为了确保我的理解不是基于一个错误的前提,我对 AoS 与 SoA 的功能和优缺点的理解,适用于具有“姓名”和“年龄”字段的“人员”记录集合与他们相关的:

数组的结构

  • 将数据存储为由多个数组组成的单个结构,例如作为People具有字段的对象Names作为字符串Ages数组和整数数组。
  • 例如,列表中第三人的信息将由类似People.Names[2]People.Ages[2]
  • 优点:
    • 当只处理来自许多“人员”记录的部分数据时,只需要从内存中加载这些数据。
    • 所述数据以同质方式存储,允许在大多数此类情况下SIMD指令更好地使用高速缓存。
  • 缺点: - 当需要一次访问多个字段时,上述优点就消失了。- 访问一个或几个对象的所有数据变得效率降低。- 大多数编程语言需要更冗长且难以读/写的代码,因为没有明确的“人”结构。

结构数组

  • 将数据存储为多个结构,每个结构都有完整的字段集,它们本身存储在所有此类结构的数组中,例如对象数组,具有People字符串字段和整数字段。PersonNameAge
  • 第三人的信息将由类似People[2].NamePeople[2].Age
  • 优点:
    • 代码是围绕一个更简单的心理模型构建的,间接性被抽象掉了。
    • 单个记录易于访问和使用。
    • 结构的存在Person使得在大多数编程语言中编写代码更加简单。
  • 缺点:
    • 当只处理大量记录中的一些数据时,需要将整个结构集加载到内存中,包括不相关的数据。
    • 结构数组不是同质的,这在这种情况下限制了 SIMD 指令可以提供的优势。

总而言之,如果您几乎只需要一次访问大量的单个字段数据 SoA 的性能可能更高,而如果您经常需要从同一个对象访问多个字段或处理单个对象而不是一次处理多个对象,那么 AoS 的性能会更高。

也就是说,我一直在阅读的一些内容似乎使情况变得混乱。首先,多个消息来源指出,SoA 需要索引寻址,据称这是低效的。我无法理解这一点,也找不到任何解释。在我看来,AoS 和 SoA 需要完全相同的操作来访问任何特定的数据,尽管顺序不同,除了 SoA 需要一个额外的指针(可能不止一个,取决于所使用的结构类型)。稍微简化一下,要在 AoS 下的上述示例中获取第五个人的年龄,您将首先获取指向数组的指针,向其添加 4,获取该数组元素的结构指针,添加大小指向它的字符串指针,因为年龄是第二个字段,然后访问该指针处的整数。在 SoA 下,

其次,我不清楚 SoA 的好处在多大程度上依赖于特定的 CPU 架构。一方面,我对上述好处的理解不依赖于任何特定的架构,除了 SIMD 指令可以提供在某些情况下在 AoS 下不可用的额外好处。另一方面,我看到有人声称 SoA 的优势可能会受到限制,具体取决于特定 SIMD 架构中可用的通道数量。同样,这似乎只影响 SIMD 指令可以提供的额外好处,而不是更一般的缓存好处。

最后,我看到了 SoA 在遍历数据时可能需要更多缓存方式的说法。我不完全确定缓存方式是什么,或者如果有的话,具体是指“遍历”数据。我最好的猜测是“缓存方式”是指关联缓存中潜在冲突的数量或与之相关,并且它与我上面提到的第二个 Con 相关。

0 投票
0 回答
293 浏览

c++ - C++ Game-engine:缓存友好的游戏对象设计

我目前正在开发 C++ 中的游戏引擎等等,我这样设计了我的游戏对象:

每个数据组件在内存中连续分配,并被推送到它们所属的管道中。在运行时,每个模块都会计算它们拥有的数据组件列表。

数据:

爱爱爱

渲染渲染渲染

物理学物理学物理学

问题是:如果它们是由多个模块使用的数据组件怎么办?例如实体的位置。在每个模块中加载它们对缓存行是否致命?

你有什么建议来改进这样的设计吗?或者我应该使用另一个?

非常感谢 !

0 投票
2 回答
277 浏览

c# - C# 中的数组结构 - 减少样板代码编写

我正在用 C# 编写游戏,并且正在使用 SoA 模式来处理性能关键的组件。

这是一个例子:

理想情况下,我希望其他程序员只指定这些数据(如上所述)并完成它。但是,必须对每个数组进行一些操作,例如分配、复制、增长等。现在我正在使用带有抽象方法的抽象类来实现这些操作,如下所示:

这要求程序员在每次编写新组件时添加所有这些样板代码,并且每次添加新数组时。

我尝试通过使用模板来解决这个问题,虽然这适用于 AoS 模式,但它对 SoA 并没有多大好处(Data : BaseData<int, float>这会非常模糊)。

所以我想听听在某处自动“注入”这些数组以减少大量样板代码的想法。

0 投票
3 回答
504 浏览

c++ - 如何使用 std::vector> 管理内存(调整大小,保留,...),但实际上保持 As before the Bs,连续

我正在开发一个实体组件系统 (ECS),灵感来自Bitsquid 的博客系列。我的 ECS 由两个主要类组成:系统(负责创建/销毁实体)和属性(负责为给定系统存储组件的 std::vector)。

创建/销毁实体时,系统会触发一些信号以使属性实例了解系统上的状态更改,以保持数据连续和良好映射。

下面是一个汽车系统的简单示例,每辆汽车都有两个属性:point2d 位置,std::string 名称。

这样我可以将数据保存在单独的“数组”中。

假设我想添加 1000 个实体。我可以调用std::vector::reserve(1000)所有属性,然后push_back对每个属性执行 1000 次。

我已经确定了这种方法的一个问题:reserves如果我有 N 个属性,我将需要 1+N 。我想知道我是否可以使用 astd::vector<tuple<point2d, std::string>>来处理内存分配,但假装管理数据(重新解释强制转换?)我将所有的point2d数据连续存储,然后是字符串。

这样我就可以利用std::vectorapi 来保存通知/保留/调整大小的操作。虽然我必须调整访问方法(如 vector::at、operator[]、begin、end)。

您对如何实现这一目标有任何想法吗?或者如果您认为这不是一个好主意,有什么替代建议?

0 投票
1 回答
161 浏览

c++ - 为什么这段代码非常慢?有什么与缓存行为有关的吗?

我开始了一些面向数据的设计实验。我最初开始做一些 oop 代码,发现有些代码非常慢,不知道为什么。这是一个例子:我有一个游戏对象

然后我使用 new 创建 1,000,000 个对象,然后循环调用 UpdateFoo()

完成循环大约需要 20 毫秒。当我注释掉float m_Pos[2]的时候发生了奇怪的事情,所以对象看起来像这样

突然循环需要大约 150 毫秒才能完成。如果我在 m_Vel 之前放任何东西,速度会快得多。我尝试在 m_Vel 和 m_Foo 或其他地方之间放置一些填充,除了 m_Vel 之前的地方......慢。

我在发布版本中测试了 vs2008 和 vs2010,i7-4790 知道这种差异是如何发生的吗?它是否与任何缓存一致的行为有关。

这是整个样本:

0 投票
2 回答
36 浏览

python - 是否可以将变量从文件导入另一个并处理然后将结果返回到第一个

首先.py

第二个.py

第一个文件中的变量(第一个)

第二个文件获取变量然后执行功能

然后第一个文件从第二个文件中获取结果并执行该功能

我想知道有没有可能!!!或者正确的选择是将变量分隔在第三个文件中,或者如果代码不长,则将所有变量合并到同一个文件中

0 投票
2 回答
3091 浏览

c++ - 了解 std::transform 以及如何击败它

我试图在一个简单、具体的问题上理解面向数据的设计。提前向面向数据的设计人员道歉,如果我做了一些非常愚蠢的事情,但我很难理解我的推理为什么以及在哪里失败。

假设我有一个简单的操作,, float_t result = int_t(lhs) / int_t(rhs)。如果我将所有变量保存在它们相应的容器中,例如std::vector<float_t>std::vector<int_t>,并且我使用std::transform,我会得到正确的结果。using float_t = float然后,对于 where和的具体示例using int_t = int16_t,我假设struct在 64 位架构上将这些变量打包在 a 中,并将它们收集到容器中应该会产生更好的性能。

我认为它struct构成了一个 64 位对象,并且对 的单个内存访问struct将为我提供所需的所有变量。另一方面,当所有这些变量都收集在不同的容器中时,我将需要三种不同的内存访问来获取所需的信息。以下是我设置环境的方法:

g++ -O3在我的机器上编译后的代码,

基本上,std::transform通过2.5x. 如果您能帮助我理解这种行为,我将不胜感激。结果是由于

  1. 我没有正确掌握面向数据的设计原则,或者,
  2. 这个非常简单的例子的一些人工制品,例如内存位置被分配得非常接近,并且在某种程度上被编译器非常有效地优化?

最后,有没有办法std::transform在这个例子中击败,或者,它是否足以成为首选解决方案?我既不是编译器优化方面的专家,也不是面向数据的设计方面的专家,因此,我自己无法回答这个问题。

谢谢!

编辑。根据@bolov 在评论中的建议,我已经更改了测试这两种方法的方式。

现在代码看起来像:

使用相应的 shell (fish) 脚本

这给出了以下内容:

我希望我已经正确理解了正确的测试方法。尽管如此,差异仍然是2-3倍。