1

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

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

4

1 回答 1

2

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

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

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

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

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

于 2013-01-22T03:03:21.313 回答