【发布时间】:2018-07-21 20:07:32
【问题描述】:
我在 Numba 中有一个函数,其中包含多个可以并行化的循环。循环写入一个公共数组 K,所以我知道编译器可能没有尽可能地优化。 但是,我不知道什么会使 numba 的 jit 编译器创建最有效的代码。文档中的示例过于简单,无法提供帮助。
我尝试将每个range 更改为prange。在k_x 上并行循环时获得了最好的结果,但我在 4 核的机器上只得到了 2.8 倍的改进。我知道我不应该期望线性性能改进,但我感觉在这种情况下我应该得到更好的结果。例如,我使用 dask,x_cond.map_blocks(cond_expect_kernel, x_tr, *args) 得到了稍微好一点的结果,考虑到调度程序的开销,这很奇怪。
除了简单地将range 更改为prange 之外,还有其他方法可以提高此函数的并行性吗?
原函数
@jit(float64[:,:](float64[:,:], float64[:,:], int64, int64), nopython=True, nogil=True)
def cond_expect_kernel(x_cond, x_tr, degree, amount_non_cond_vars):
size = x_cond.shape[1]
x_tr_cond = x_tr[:, :size]
samples_x = x_cond.shape[0]
samples_tr = x_tr.shape[0]
K = (1+np.dot(x_cond, x_tr_cond.T))**degree
for j in range(size, size+amount_non_cond_vars):
for k_x in range(samples_x):
for k_x_tr in range(samples_tr):
K[k_x, k_x_tr] += x_tr[k_x_tr, j]**2*3
for j_left in range(size):
K[k_x, k_x_tr] += x_cond[k_x, j_left]*x_tr[k_x_tr, j_left]*x_tr[k_x_tr, j] ** 2 *3
return K
迄今为止最好的并行版本:
@jit(float64[:,:](float64[:,:], float64[:,:], int64, int64), nopython=True, parallel=True)
def cond_expect_kernel_parallel(x_cond, x_tr, degree, amount_non_cond_vars):
size = x_cond.shape[1]
x_tr_cond = x_tr[:, :size]
samples_x = x_cond.shape[0]
samples_tr = x_tr.shape[0]
K = (1+np.dot(x_cond, x_tr_cond.T))**degree
for j in range(size, size+amount_non_cond_vars):
for k_x in prange(samples_x):
for k_x_tr in range(samples_tr):
K[k_x, k_x_tr] += x_tr[k_x_tr, j]**2*3
for j_left in range(size):
K[k_x, k_x_tr] += x_cond[k_x, j_left]*x_tr[k_x_tr, j_left]*x_tr[k_x_tr, j] ** 2 *3
return K
作为参考,我正在开发一台 4 核的机器和另一台 16 核的机器。 samples_x 约为 100,000,samples_tr 约为 50000,size 约为 3,amount_non_cond_vars 约为 100。
谢谢!
【问题讨论】: