我正在阅读这篇文章,这个人继续谈论如何通过与OOP混合使用面向数据的设计,每个人都能从中受益匪浅.但是,他没有显示任何代码示例.
我搜索了这个,但是找不到任何关于这是什么的真实信息,更不用说任何代码示例了.有人熟悉这个术语并且可以提供一个例子吗?对于别的东西,这可能是一个不同的词吗?
还有一个问题是关于什么是面向数据的设计,并且有一篇文章经常被提及(我已经读了5到6次).我理解这个的一般概念,特别是在处理例如3d模型时,你想要将所有顶点保持在一起,而不是用法线污染你的脸等.
但是,我确实很难想象数据导向设计如何适用于除最简单的案例(3d模型,粒子,BSP树等)以外的任何其他设计.是否有任何好的例子真正包含数据导向设计并展示了它在实践中如何运作?如果需要,我可以通过大型代码库.
我特别感兴趣的是"哪里有一个很多"的咒语,我似乎无法与其他人联系.是的,总有不止一个敌人,但是,你仍然需要单独更新每个敌人,因为他们现在不会以同样的方式移动它们吗?这同样适用于在接受的答案上述问题的"balls',例如(其实我到这个问题的答案评论问这一点,但还没有得到答复还).仅仅是渲染只需要位置,而不是速度,而游戏模拟需要两者,而不是材料?或者我错过了什么?也许我已经理解了它,这是一个比我想象的更直接的概念.
任何指针将非常感谢!
using namespace std;
Run Code Online (Sandbox Code Playgroud)
考虑实体/对象管理的传统OOP方法:
struct Entity { bool alive{true}; }
struct Manager {
vector<unique_ptr<Entity>> entities; // Non cache-friendly
void update() {
// erase-remove_if idiom: remove all !alive entities
entities.erase(remove_if(begin(entities), end(entities),
[](const unique_ptr<Entity>& e){ return !e->alive; }));
}
};
struct UserObject {
// Even if Manager::entities contents are re-ordered
// this reference is still valid (if the entity was not deleted)
Entity& entity;
};
Run Code Online (Sandbox Code Playgroud)
但是,我想尝试一种面向数据的方法:不动态分配Entity实例,而是将它们存储在缓存友好的线性内存中.
struct Manager {
vector<Entity> entities; // Cache-friendly
void …Run Code Online (Sandbox Code Playgroud) 来自C/C++的背景,关于减少缓存未命中的对象的内存布局是特别在处理控制台时尤其重要的.面向数据的设计通常比面向对象的设计更受青睐,以帮助保持相关对象在内存中彼此靠近(特别是在性能关键区域).
最近,我一直在做一些Javascript开发,我想知道Javascript社区中普遍的共识是什么.
由于我在Javascript方面的经验有限,我经常惊讶于在分析时看到完全出乎意料的结果.从浏览器到浏览器,Javascript对象/结构的内部存储器布局和实现变化很大,我想知道是否值得尝试优化.
我在jsPerf上创建了一个简单的测试用例(http://jsperf.com/object-vs-data)来比较这两种方法的性能,虽然它显示了Chrome的性能提升,但是Safari没有明显的加速.
在Javascript中,我是否应该关注对象的内存布局?或者它更像是"以一种方式实施,然后根据需要进行优化"类型的东西?
第二种选择似乎是浪费(就开发时间而言),特别是如果有一些好的指导方针可以遵循.
谢谢〜
补充信息:这基本上是我如何在Javascript中实现这两种方法.上面的jsPerf测试用例就是这样实现的.
var objectOriented = [
{ foo: 1, bar: 2 },
{ foo: 3, bar: 4 }
];
var dataOriented = {
foos: [1, 3],
bars: [2, 4]
};
// Object-oriented access:
var a = objectOriented[0].bar;
// Data-oriented access:
var b = dataOriented.bars[0];
Run Code Online (Sandbox Code Playgroud) 我最近一直在阅读AoS vs SoA结构设计和面向数据的设计.很难找到关于这两者的信息,而且我发现的东西似乎比我拥有更多的处理器功能.也就是说,我对前一个主题的理解特别导致了一些我认为应该能够理解答案的问题.
首先,为了确保我的理解不是基于错误的前提,我对AoS vs SoA的功能和利弊的理解,应用于具有'Name'和'Age'字段的'Person'记录的集合与他们相关:
People字段Names作为字符串Ages数组和整数数组作为对象.People.Names[2]和People.Ages[2]People数组Person,它们具有Name字符串字段和Age整数字段.People[2].Name和People[2].AgePerson结构的存在使得在大多数编程语言中编写代码变得更加简单.它的长短似乎是,假设为了论证,你的性能瓶颈是数据访问和编码的简易性是无关紧要的,如果你几乎完全需要一次访问大量的单个字段数据SoA可能更具性能,而如果您经常需要从同一个对象访问多个字段或处理单个对象而不是一次处理多个字段,AoS将更具性能.
也就是说,我一直在阅读的一些内容似乎让图片变得混乱.首先,多个消息来源已经声明SoA需要索引寻址,据称这是低效的.我无法理解这一点,也无法找到任何解释.在我看来,AoS和SoA需要完全相同的操作来访问任何特定的数据,尽管顺序不同,除了SoA需要一个额外的指针(可能多于一个,取决于所使用的结构类型).稍微简化一下,为了在AoS下面的上面例子中得到第五个人的年龄,你首先得到指向数组的指针,向它添加4,在数组的那个元素处获取结构指针,添加一个大小字符串指向它,因为age是第二个字段,然后访问该指针处的整数.在SoA下,您将获得指向结构的指针并向其添加字符串数组指针的大小以获取年龄列表,然后获取指向存储在那里的整数列表的指针并向其添加4,然后获取整数存储在那里.
其次,我不清楚SoA的好处在多大程度上取决于特定的CPU架构.一方面,我对上述优点的理解并不依赖于任何特定的体系结构,除了SIMD指令在某些情况下可以提供AoS下无法提供的额外好处.另一方面,我看到声称可以限制SoA的优势,具体取决于特定SIMD架构中可用的通道数量.同样,这似乎只会影响SIMD指令可以提供的更多通用缓存优势的额外好处.
最后,我已经看到SoA在遍历数据时需要更多缓存方式的说法.我不完全确定缓存方式是什么或者什么,如果有的话,特别是'遍历'数据.我最好的猜测是"缓存方式"指的是关联缓存中潜在冲突的数量或与之相关,并且它与上面提到的第二个Con相关.
我正在为游戏引擎开发实体组件系统.我的目标之一是使用面向数据的方法来实现最佳数据处理.换句话说,我想遵循指导而不是希望结构的结构而不是结构数组.但是,我的问题是我还没有想出一个巧妙的方法来解决这个问题.
到目前为止,我的想法是系统中的每个组件都负责游戏逻辑的特定部分,比如重力组件根据质量,速度等来处理每帧的计算力,而其他组件则负责其他组件.因此,每个组件都对不同的数据集感兴趣.重力组件可能对质量和速度感兴趣,而碰撞组件可能对边界框和位置等感兴趣.
到目前为止,我想我可以拥有一个数据管理器,每个属性保存一个数组.因此,假设实体可能具有权重,位置,速度等中的一个或多个,并且它们将具有唯一ID.数据管理器中的数据将表示如下,其中每个数字代表一个实体ID:
weightarray -> [0,1,2,3]
positionarray -> [0,1,2,3]
velocityarray -> [0,1,2,3]
Run Code Online (Sandbox Code Playgroud)
如果所有实体都具有每个属性,则此方法很有效.但是,如果只有实体0和2具有所有树属性,而其他属性是不移动类型的实体,则它们将没有速度并且数据看起来如下:
weightarray -> [0,1,2,3]
positionarray -> [0,1,2,3]
velocityarray -> [0,2] //either squash it like this
velocityarray -> [0 ,2 ] //or leave "empty gaps" to keep alignment
Run Code Online (Sandbox Code Playgroud)
突然间,迭代它并不容易.如果我采用第二种方法,那么只对迭代和操纵速度感兴趣的组件必须以某种方式跳过空隙.保持阵列短路的第一种方法在更复杂的情况下也不能很好地工作.假设我有一个具有所有三个属性的实体0,另一个实体1仅具有权重和位置,以及实体2仅具有位置和速度.最后,最后一个实体3只有重量.压扁的阵列看起来像:
weightarray -> [0,1,3]
positionarray -> [0,1,2]
velocityarray -> [0,2]
Run Code Online (Sandbox Code Playgroud)
另一种方法会留下这样的空白:
weightarray -> [0,1, ,3]
positionarray -> [0,1,2, ]
velocityarray -> [0, ,2, ]
Run Code Online (Sandbox Code Playgroud)
如果您只想迭代仅具有一些属性的实体集,则这两种情况都是重要的迭代.例如,给定的组件X将对处理具有位置和速度的实体感兴趣.如何提取可迭代的数组指针以使该组件进行计算?我想给它一个数组,其中元素彼此相邻,但这似乎是不可能的.
我一直在考虑解决方案,例如为每个阵列设置一个位字段,描述哪些点有效以及哪些是间隙,或者是将数据复制到没有空洞然后被提供给组件的临时阵列的系统,以及其他想法但没有我想到的是优雅的并且没有额外的处理开销(例如额外检查数据是否有效,或额外的数据复制).
我在这里问,因为我希望你们中的某个人可能有类似的经历,或者可能有想法或想法有助于解决这个问题.:)此外,如果这整个想法是垃圾,不可能正确,你有一个更好的想法,请告诉我.希望这个问题不会太长或太杂乱.
谢谢.
我有一个这样的树结构:一个模型有一个根节点,每个节点有任意数量的子节点和任意数量的网格.
在我的应用程序中,很多时候都花费遍历这个树并进行计算,如视图截顶剔除和矩阵乘法.目前,它是天真地实现的,其中每个节点具有子节点和网格的向量,并且递归地遍历树.这很慢.
我一直在关注面向数据的设计,我喜欢它对缓存非常友好的想法.我一直在想这样的事情:
struct Mesh
{
// misc data
MeshID mMeshID;
}
// probably needs more information?
struct Node
{
// begin and end index into Models 'mNodes'
uint32_t mChildrenBegin;
uint32_t mChildrenEnd;
// as above but for meshes
uint32_t mMeshesBegin;
uint32_t mMeshesEnd;
}
struct Model
{
std::vector<Node> mNodes;
std::vector<Mesh> mMeshes;
}
Run Code Online (Sandbox Code Playgroud)
现在我需要遍历树以获得可见网格列表.在每个节点,我必须检查节点是否可见.以下分支机构:
树是静态的.在应用程序中加载模型后,树永远不会更改.所以我必须能够使用这些信息来获得有效的结构.
我很困惑如何处理这个问题.
几个问题;
我试图在一个简单的特定问题上理解面向数据的设计.如果我正在做一些非常愚蠢的事情,请提前向面向数据的设计人员道歉,但我很难理解为什么以及我的推理失败的原因.
假设我有一个简单的操作,即,float_t result = int_t(lhs) / int_t(rhs).如果我将所有变量保存在相应的容器中,例如,std::vector<float_t>和std::vector<int_t>,并且我使用std::transform,我得到正确的结果.然后,对于一个具体的例子,其中using float_t = float与using int_t = int16_t我假定包装内的这些变量struct,在一个64位架构,并加以收集在容器内应该产生更好的性能.
我认为struct组成一个64位对象,并且单个内存访问struct将给我所需的所有变量.另一方面,当所有这些变量都收集在不同的容器中时,我将需要三种不同的内存访问来获取所需的信息.以下是我设置环境的方法:
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <vector>
using namespace std::chrono;
template <class float_t, class int_t> struct Packed {
float_t sinvl;
int_t s, l;
Packed() = default;
Packed(float_t sinvl, int_t s, int_t l) : sinvl{sinvl}, s{s}, l{l} {}
void comp() …Run Code Online (Sandbox Code Playgroud) 我正在努力抓住面向数据的设计,以及如何最好地编写缓存.基本上有两种情况我无法确定哪种更好以及为什么 - 有一个对象向量或带有对象原子数据的几个向量更好吗?
A)对象矢量示例
struct A
{
GLsizei mIndices;
GLuint mVBO;
GLuint mIndexBuffer;
GLuint mVAO;
size_t vertexDataSize;
size_t normalDataSize;
};
std::vector<A> gMeshes;
for_each(gMeshes as mesh)
{
glBindVertexArray(mesh.mVAO);
glDrawElements(GL_TRIANGLES, mesh.mIndices, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
....
}
Run Code Online (Sandbox Code Playgroud)
B)具有原子数据的载体
std::vector<GLsizei> gIndices;
std::vector<GLuint> gVBOs;
std::vector<GLuint> gIndexBuffers;
std::vector<GLuint> gVAOs;
std::vector<size_t> gVertexDataSizes;
std::vector<size_t> gNormalDataSizes;
size_t numMeshes = ...;
for (index = 0; index++; index < numMeshes)
{
glBindVertexArray(gVAOs[index]);
glDrawElements(GL_TRIANGLES, gIndices[index], GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
....
}
Run Code Online (Sandbox Code Playgroud)
哪一个更有内存效率和缓存友好性,导致更少的缓存未命中和更好的性能,为什么?
在许多关于面向数据的设计的着作中起着突出作用的一个特征是,在很多情况下,而不是AoS(结构数组):
struct C_AoS {
int foo;
double bar;
};
std::vector<C_AoS> cs;
...
std::cout << cs[42].foo << std::endl;
Run Code Online (Sandbox Code Playgroud)
在SoA(数组结构)中安排一个数据更有效:
struct C_SoA {
std::vector<int> foo;
std::vector<double> bar;
};
C_SoA cs;
...
std::cout << cs.foo[42] << std::endl;
Run Code Online (Sandbox Code Playgroud)
现在我正在寻找的是一个解决方案,它允许我在不改变调用接口的情况下在AoS和SoA之间切换,即我可以用最小的努力并且没有额外的运行时成本(至少到了过度的间接点),cs[42].foo;无论我正在使用哪种数据排列,都要调用.
我应该注意上面的示例语法是理想的情况,这可能是不可能的,但我也对近似近似非常感兴趣.任何接受者?