并行网格点分桶算法:低同步开销的已发表方案咨询
基于网格单元格的点分组排序:低同步多线程算法调研
问题背景
我们有一组按网格模式排列的2D/3D单元格,以及分布在该网格中的点集:每个点归属于某一单元格,部分单元格包含多个点。需要完成两个核心目标:
- 对原始点数组进行重排,使同一单元格内的所有点在内存中连续存储
- 获取每个单元格对应的点数组范围(起始、结束索引)
单线程朴素实现
伪代码如下:
for p in points: cell_id = cell_id(p) cell_counts[cell_id] += 1 cumulative_counts = add_cumulative_integers(cell_counts) starts = cumulative_counts for p in points: cell_id = cell_id(p) new_points[cumulative_counts[cell_id]] = p cumulative_counts[cell_id] += 1 ends = cumulative_counts
核心逻辑:先统计每个单元格包含的点数量,计算前缀和得到各单元格的起始偏移,最后根据偏移量将点重排到目标数组中。
低同步多线程已发表算法
针对多线程场景,已有多种成熟算法可最小化同步开销,核心思路都是通过原子操作、局部计算或任务划分来减少全局锁依赖:
1. 原子计数+并行前缀和算法
这是最直接的优化方向,对应单线程逻辑的并行化:
- 计数阶段:每个线程独立处理一部分点,使用原子操作(如
fetch_add)更新对应单元格的计数,无需全局锁。原子操作的粒度仅为单个单元格的计数变量,同步开销远低于全局锁。 - 前缀和阶段:采用并行扫描算法(如Hillis-Steele扫描、Blelloch扫描),这类算法能在O(log n)时间复杂度内完成全局前缀和的并行计算,仅需少量线程间同步。
- 重排阶段:每个线程再次处理自己负责的点集,根据预先计算的起始偏移,用原子操作获取当前单元格的写入位置并完成写入,避免跨线程写入冲突。
2. 局部计数+批量合并算法
该思路通过减少同步次数来降低开销:
- 局部计数:每个线程先维护一份本地的单元格计数副本,处理完分配给自己的点集后,再将本地计数批量合并到全局计数数组中(合并时使用原子操作或细粒度锁)。这样每个线程仅在合并阶段进行同步,而非每次计数都触发同步。
- 并行前缀和:同样使用并行扫描算法计算全局起始偏移。
- 无冲突重排:由于每个点的目标位置由全局起始偏移和单元格内计数唯一确定,线程可直接写入对应位置,无需额外同步。
3. 基于样本排序的单元格分组算法
将单元格分组问题转化为并行排序问题,利用成熟的并行排序框架:
- 采样划分:先从点集中采样部分点的单元格ID,确定划分阈值,将整个单元格ID范围划分为多个连续区间。
- 并行处理:每个线程负责处理属于某一区间的点,直接将这些点排序后写入预先分配好的内存区域。区间边界预先确定,线程间无写入冲突,无需同步。
- 结果合并:由于区间是连续的,最终同一单元格的点自然连续存储,直接提取各单元格的起始、结束索引即可。
内容的提问来源于stack exchange,提问作者Makogan
相关产品推荐
相关产品推荐

