python - 如何使用 Python/Numba 对数组执行并行归约
问题描述
我正在有效地寻找解决这个问题的方法。我了解将较大的数组减少为较小的数组(例如,如果您进行了某种粒子模拟,并且您正在将矩与网格点数组相加)会导致竞争条件,因为目标数组在被访问时会被线程锁定,导致其他线程丢弃值并导致错误输出。一种简单的解决方法是让每个线程在目标数组的副本上累积其值(因为可能只有数百个网格点,但有数百万个粒子),然后将这些数组添加到并行循环之外。
但是,我不知道从哪里开始。我的问题基本上归结为“如何为每个线程创建一个数组的本地副本,执行一些操作,并在最后总结它们?”。一个简化的例子可能是:
def deposit_particles_to_grid(ploc, pweight, grid):
'''
ploc : (N) : Particle location as an index in the grid array, leftmost point
pweight : (Nx3) : Particle weights as a float across 3 gridpoints
grid : (M) : Target array gridpoints where M << N
'''
for ii in nb.prange(ploc.shape[0]):
grid[ploc[ii]] += pweight[ii, 0]
grid[ploc[ii] + 1] += pweight[ii, 1]
grid[ploc[ii] + 2] += pweight[ii, 2]
return
在这里,ploc.shape[0] 可能在几千万或几亿,而 grid 可能只有几百或几千个点。理想情况下,最好将 ploc 和 pweight 数组“分块”到 n_threads 中,并为每个线程提供自己的“网格”副本。我只是不确定该怎么做。
编辑示例以更能代表我当前的非并行代码。每个点不只是一对一的直方图,而是分布在 pweight 中定义的 3 个连续点上。
解决方案
回答我自己的问题,因为对于像我这样不熟练的人来说,这可能很有用。基本上我所做的只是为每个线程创建一个带有额外轴的新数组,并在最后在该轴上求和。这使值的沉积并行化(这是最密集的部分),并防止竞争条件,因为每个线程都有自己的轴。
我没有声明这是否可以大规模扩展或任何东西,但就我的目的而言,它工作得很好。不过,我欢迎对此进行任何改进!我知道如果预先分配了 grid_threads 内存(在可能多次调用此函数的情况下),则可以加快速度。
@nb.njit(parallel=True)
def deposit_particles_to_grid_parallel(ploc, pweight, grid):
'''
ploc : (N) : Particle location as an index in the grid array, leftmost point
pweight : (Nx3) : Particle weights as a float across 3 gridpoints
grid : (M) : Target array gridpoints where M << N
'''
grid_threads = np.zeros((grid.shape[0], n_threads), dtype=grid.dtype)
N_per_thread = ploc.shape[0] / n_threads
n_start_idxs = np.arange(n_threads)*N_per_thread
for tt in nb.prange(n_threads):
for ii in range(n_start_idxs[tt], n_start_idxs[tt]+N_per_thread):
grid_threads[ploc[ii], tt] += pweight[ii, 0]
grid_threads[ploc[ii] + 1, tt] += pweight[ii, 1]
grid_threads[ploc[ii] + 2, tt] += pweight[ii, 2]
grid[:] = grid_threads.sum(axis=1)
return
推荐阅读
- xml - XSLT 建议:确保数字是 ## 并删除不必要的零
- php - 无法使用 $ajax() 在 PHP 中上传文件
- java - 实例化参数类型的空对象
- javascript - 为什么我使用 Datatables 的 Rails 应用程序在我使用后退按钮时无法正确绘制表格,但在我刷新页面时却如此?
- javascript - 允许括号内的整数
- java - 必须自动将特定字符串从文本字段突出显示到文本区域
- apache-spark - 如何在火花结构化流中使用 scikit pickle 模型?
- arrays - Swift Vapor错误解码对象数组
- mailkit - 使用 IMAP 对电子邮件进行分组的最佳方式?在同一个邮箱中,我需要将电子邮件与项目 ID 相关联
- javascript - 无法基于 onClick 更改图像