【问题标题】:OpenCL Particle system memory layoutOpenCL 粒子系统内存布局
【发布时间】:2013-01-22 21:52:52
【问题描述】:

我需要一些关于 OpenCL 代码的建议。我在 OpenCL 中编写一个粒子系统,直接从 OpenCL 绘制到 gpu 上,所以没有复制到 CPU。这一切都很好,但我在创建新粒子时遇到了问题。

我在 GPU 上分配了一个包含所有粒子数据的大内存区域。粒子的参数之一称为 isAlive,它决定了它的活跃天气。当我想创建一个新粒子时,我会找到一个不活跃的粒子,将其位置修改为起始位置,然后将其设置为 isAlive true。这个过程非常昂贵,因为我必须遍历所有粒子以找到不活跃的粒子,同时我必须确保多个线程不会同时创建相同的粒子(所以我不最后得到更多的粒子然后我问)。 有没有什么好的考虑、算法或策略可以更优雅、更快地解决这个问题?

【问题讨论】:

  • 所以您不是将数据复制回主机,而是在每个时间步从主机调用不同的内核?还有,一旦一个粒子死了,除了被一个新粒子替代之外,它还能复活吗?
  • 是的,我从不将数据复制回 CPU。我重复一系列内核调用以在每个时间步更新粒子。是的,我一次又一次地重用死粒子,但它作为物理世界中的一个新粒子,只是替换旧的内存区域,所以它与旧的死粒子没有任何联系。
  • 你知道给定时间死粒子的数量吗?您是否有足够的内存来分配位数组来跟踪活动状态?你的目标总共有多少个粒子?您是同时替换所有死粒子,还是可以等到稍后的时间步长?
  • 我可以很容易地找到死粒子的数量,我认为是的。是的,我确实有更多可用内存。迭代位数组然后在大数组中查找是否会更快?粒子如何死亡和生存是非常不同的。它是一些图形的交互式引擎。有时我需要用大量粒子快速填充屏幕。

标签: opencl


【解决方案1】:

这是我会尝试的一种方法:将 isAlive 的标志与数据结构的其余部分分开。这似乎是一段经常读取但很少写入的数据。使用单个 uint 跟踪 32 个粒子的状态。使用 0 表示活着,使用 1 表示死亡——本质上是创建一个 isDead 列表。我假设你将拥有比死粒子更多的活粒子。

可以在需要时将这些值(一次 32 个)读取到本地内存中。这使您可以创建一个快速迭代数据的内核,以寻找非零值。这里的性能大幅提升伴随着密集数据,从而减少了存储和加载标志的内存开销。这使得检查其中一个值成为一种更便宜的操作,允许您更快地迭代它们。更改 32 位值时需要小心,以免损坏共享同一 uint 的其他数据(隔行扫描可以帮助解决此问题)。当您需要缩小 1 位的确切位置时,指令 clz 和 popcount 将很有帮助。 opencl 1.2 refcard

可能的优化#1: 如果您愿意,您可以尝试将这些值交错,以便第一个 uint 跟踪索引 0,32,64,96,...,992,第二个 uint 表示 1,33,65,97,...,993 和很快。这可以允许通常作用于特定粒子的工作项读取 32 个连续的 isDead 状态。这可能会付出更多的努力而不值得,但这取决于您的应用程序。

可能的优化 #2: 如果死粒子真的很稀疏,那么在更高级别上跟踪 isDead 列表可能是值得的。使用相同的技术,很容易将 isDead 位/uint 列表再次减少 32 倍。第 2 级的每个位代表相应 uint 的状态。即:如果设置了 uint N 中的任何位,则该列表的位 N 也将被设置。仅当您的数据中预期有很多零时才有用,但是这个额外的步骤可以节省大量在数据中搜索稀有“on”位的周期。包括原始 isDead 数据在内的总内存开销为:memBits = ceil(particleCount/32) + ceil(particleCount/32^2),或每 2^20 个粒子大约 128kb + 4kb。

使用上述方法,可以编写一个内核,返回给定范围内的死粒子数,并快速找到下一个可用的死粒子。

【讨论】:

  • 非常感谢您提供所有这些信息!我会尝试这种方法。
猜你喜欢
  • 1970-01-01
  • 2011-02-09
  • 1970-01-01
  • 2013-07-16
  • 2011-04-14
  • 1970-01-01
  • 1970-01-01
  • 2012-06-28
  • 1970-01-01
相关资源
最近更新 更多