Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading cython中for循环的并行化:超越prange_Multithreading_Multiprocessing_Cython_Python Multiprocessing - Fatal编程技术网

Multithreading cython中for循环的并行化:超越prange

Multithreading cython中for循环的并行化:超越prange,multithreading,multiprocessing,cython,python-multiprocessing,Multithreading,Multiprocessing,Cython,Python Multiprocessing,我正在努力使用cython正确地并行化函数。基本上,问题在于如何存储一些数据。实际代码有点长,但最终它会执行以下操作: def bin_var(double[:] dist, double[:] values, double[:] bin_def, double[:] varg, long[:] count): dbin = (bin_def[1] - bin_def[0]) / bin_def[2] f

我正在努力使用cython正确地并行化函数。基本上,问题在于如何存储一些数据。实际代码有点长,但最终它会执行以下操作:

def bin_var(double[:] dist,
            double[:] values,
            double[:] bin_def,
            double[:] varg, long[:] count):

    dbin = (bin_def[1] - bin_def[0]) / bin_def[2]

    for n1 in range(values.size):
            if (dist[n1] < bin_def[0]) or (dist[n1] >= bin_def[1]):
                continue
            else:
                ni = int((dist - bin_def[0]) / dbin)
                count[ni] += 1
                varg[ni] += calc_something(values[ni])

    # compute the mean
    for n1 in range(int(bin_def[2])):
        varg[ni] /= count[ni]
这段代码适用于一些简单的并行化值,并且dist非常大:需要将第一个for循环拆分到单独的进程上,每个进程处理自己版本的count和varg数组。完成后,必须在第二个for循环之前将count和varg的不同版本相加,从而将所有内容组合在一起

也就是说,我花了两天的时间试图理解如何在cython中高效地实现这一点,我开始怀疑这在当前版本的语言中是不可能的。请注意,对于第一个循环仅使用cython.parallel中的prange并不能提供正确的结果,因为我假设从不同线程同时访问ni、count和varg


cython并行支持真的如此有限吗?我得到了如此好的单线程加速,我只希望我能继续…

我可以在这里想到三个选项:

使用GIL确保+=是单线程完成的:

varg_ni = calc_something(values[ni]) # keep this out 
               # of the single threaded block...
with gil:
    count[ni] += 1
    varg[ni] += varg_ni
这很简单,如果在calc_中完成的工作相当大,也不会太糟糕

使用count和varg 2D数组,每个线程写入不同的列。然后沿第二维度求和:

# rough, untested outline....

# might need to go in a `with parallel()` block
num_threads = openmp.omp_get_num_threads()

cdef double[:,:] count_tmp = np.zeros((count.shape[0],num_threads))
cdef double[:,:] varg_tmp = np.zeros((varg.shape[0],num_threads))

# then in the loop:
count_tmp[ni,cython.parallel.threadid()] += 1
varg_tmp[ni,cython.parallel.threadid()] += calc_something(values[ni])

# after the loop:
count[:] = np.sum(count_tmp,axis=1)
varg[:] = np.sum(varg_tmp,axis=1)
您也可以使用中的想法来做类似的事情

注意-GCC目前为此给了我一个内部编译器错误-我觉得它应该可以工作,但目前它似乎不工作,所以请尝试选项3,风险自负。。。使用以原子方式进行添加。这需要一些工作来绕过Cython,但应该不会太难。使用add_in place宏创建短C头文件:

_Pragma是一个C99特性,它应该允许您将Pragma放入预处理器语句中。然后告诉Cython关于该头文件的信息,就像它是一个函数一样:

cdef extern from "header.h":
    void add_inplace(...) nogil # just use varargs to make Cython think it accepts anything
然后在循环中执行以下操作:

add_inplace(count[ni], 1)
add_inplace(varg[ni], calc_something(values[ni]))
因为这使用了宏技巧,所以它可能有点脆弱,即肯定不能与PyObject*s一起使用,但在使用标准C数字类型时,它应该生成正确的C代码。检查代码以确保


谢谢,我试过gil way,但它对我的calc_来说太慢了,我甚至没有提到它。另一方面,其他的选择似乎很有希望。解决方案2对我来说是可行的,但它只值得我为那些我真正感兴趣的大问题付出努力。但是请注意,prange有一些奇怪的行为,比如在循环之后强制返回。我没有尝试解决方案3,因为它对我来说有点遥不可及。我无法让解决方案3发挥作用,因此我认为建议您避免:
add_inplace(count[ni], 1)
add_inplace(varg[ni], calc_something(values[ni]))