【发布时间】:2021-06-27 16:27:32
【问题描述】:
我尝试优化搜索两个numpy 与numba 向量之间的最小值。在我使用 prange 和 parallel=True 选项之前,速度会加快并且结果是正确的。我知道问题在于在并行执行期间共享变量min_val、tmp、min_val_idx_a、min_val_idx_b(可能使用并行线程)。有没有办法克服这个问题并将numba 与parallel=True 选项一起使用? (这让我的简单代码快了 300 倍)
import numpy as np
from numba import jit, int32, void, double, prange
@jit(void(double[:], double[:], int32), nopython=True, parallel=True)
def lowest_value_numba(a, b, n):
# initialization
min_val_idx_a, min_val_idx_b = 0, 0
min_val = tmp = np.abs(a[0]-b[0])
for i in prange(n):
# print(i)
for j in prange(i, n):
tmp = np.abs(a[i]-b[j])
if(tmp < min_val):
min_val = tmp
min_val_idx_a = i
min_val_idx_b = j
print(min_val, min_val_idx_a, min_val_idx_b)
n = int(1e4)
a = np.random.uniform(low=0.0, high=1.0, size=n)
b = np.random.uniform(low=0.0, high=1.0, size=n)
# setting min value by setting the same valu efor a[n-1] and b[n-1]
a[n-1], b[n-1] = 1, 1
%timeit -n 1 -r 1 lowest_value_numba(a, b, n)
输出不正确(应该是0.0 9999 9999):
0.23648058275546968 0 0
223 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
但是对于使用parallel=False 的编译输出是正确的(最后的值彼此最接近):
0.0 9999 9999
65 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
【问题讨论】:
-
我可以通过将
min_val_idx_a, min_val_idx_b = 0, 0替换为一个包含两个零值的numpy 数组来实现并行工作。然后我更新了数组中的值。但是,尝试多次运行 timeit 会产生不一致的结果......所以我认为这不是完整的答案。 -
@jakub 谢谢,对我来说结果是一致的
-
@jakub 抱歉,有一些错误:
0.0 9999 9999,2.2752791872804323e-08 1581 5090,0.0 9999 9999,0.0 9999 9999 -
numba parallel documentation 有有用的注释。它指出“用户需要确保循环没有交叉迭代依赖项,除了支持的减少。”由于当前的实现具有交叉迭代依赖性,因此可能可以根据那些支持的缩减来重新构建原始问题。
-
我认为,问题在于您可能会在每次迭代时覆盖索引。这不是减少方法,例如
+=。对于执行+=的循环,numba 可以并行计算多个总和,最后将所有这些总和求和。在当前问题中,索引被覆盖。你看得到差别吗?并行线程(不确定它们是否是线程)如何知道如何减少它们计算的值。似乎是一种竞争条件。
标签: python numpy parallel-processing jit numba