如何在 CUDA 应用程序中构建数据以获得最佳速度

2024-04-08

我正在尝试编写一个简单的粒子系统,利用 CUDA 来更新粒子位置。现在,我定义的粒子有一个对象,该对象的位置由三个浮点值定义,速度也由三个浮点值定义。更新粒子时,我向速度的 Y 分量添加一个常量值以模拟重力,然后将速度添加到当前位置以得出新位置。就内存管理而言,最好维护两个独立的浮点数组来存储数据或以面向对象的方式进行结构。像这样的事情:

struct Vector
{
    float x, y, z;
};

struct Particle
{
    Vector position;
    Vector velocity;
};

似乎两种方法的数据大小都是相同的(每个浮点数 4 个字节,每个向量 3 个浮点数,每个粒子 2 个向量,总共 24 字节),看起来 OO 方法将允许 CPU 和GPU,因为我可以使用单个内存复制语句而不是 2 个(从长远来看,更多,因为还有一些有关粒子的其他信息将变得相关,例如年龄、寿命、重量/质量、温度等)此外,代码的简单可读性和易于处理也使我倾向于面向对象方法。但我看到的例子没有利用结构化数据,所以这让我想知道是否有原因。

所以问题是哪个更好:单独的数据数组还是结构化对象?


在数据并行编程中,谈论“数组结构”(SOA) 与“结构数组”(AOS) 是很常见的,其中两个示例中的第一个是 AOS,第二个是 SOA。许多并行编程范例,特别是 SIMD 样式范例,会更喜欢 SOA。

在 GPU 编程中,通常首选 SOA 的原因是优化对全局内存的访问。您可以在以下位置查看录制的演示文稿高级 CUDA C http://nvidia.fullviewmedia.com/GPU2009/1002-gold-1086.html去年来自 GTC 的 GPU 如何访问内存的详细描述。

要点是内存事务的最小大小为 32 字节,并且您希望最大化每个事务的效率。

使用 AOS:

position[base + tid].x = position[base + tid].x + velocity[base + tid].x * dt;
//  ^ write to every third address                    ^ read from every third address
//                           ^ read from every third address

使用 SOA:

position.x[base + tid] = position.x[base + tid] + velocity.x[base + tid] * dt;
//  ^ write to consecutive addresses                  ^ read from consecutive addresses
//                           ^ read from consecutive addresses

在第二种情况下,从连续地址读取意味着您的效率为 100%,而第一种情况为 33%。请注意,在较旧的 GPU(计算能力 1.0 和 1.1)上,情况要糟糕得多(效率为 13%)。

还有另一种可能性 - 如果结构中有两个或四个浮点数,那么您可以以 100% 的效率读取 AOS:

float4 lpos;
float4 lvel;
lpos = position[base + tid];
lvel = velocity[base + tid];
lpos.x += lvel.x * dt;
//...
position[base + tid] = lpos;

请再次查看 Advanced CUDA C 演示文稿以了解详细信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 CUDA 应用程序中构建数据以获得最佳速度 的相关文章

随机推荐