这是我会尝试的一种方法:将 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。
使用上述方法,可以编写一个内核,返回给定范围内的死粒子数,并快速找到下一个可用的死粒子之一。