Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vue.js/6.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
Python Cython平行度和模板_Python_Cython - Fatal编程技术网

Python Cython平行度和模板

Python Cython平行度和模板,python,cython,Python,Cython,在大量使用了numba之后,我将回到cython来并行化一些耗时的函数。以下是一个基本示例: import numpy as np cimport numpy as np from cython import boundscheck, wraparound from cython.parallel import parallel, prange @boundscheck(False) @wraparound(False) def cytest1(double[:,::1] a, double

在大量使用了numba之后,我将回到cython来并行化一些耗时的函数。以下是一个基本示例:

import numpy as np
cimport numpy as np

from cython import boundscheck, wraparound
from cython.parallel import parallel, prange

@boundscheck(False)
@wraparound(False)
def cytest1(double[:,::1] a, double[:,::1] b, int ix1, int ix2, int iz1, int iz2):

    cdef int ix
    cdef int iz

    for ix in range(ix1, ix2):
        for iz in range(iz1, iz2):
            b[ix, iz] = 0.5*(a[ix+1, iz] - a[ix-1, iz])
    return b


@boundscheck(False)
@wraparound(False)
def cytest2(double[:,::1] a, double[:,::1] b, int ix1, int ix2, int iz1, int iz2):

    cdef int ix
    cdef int iz

    with nogil, parallel():
        for ix in prange(ix1, ix2):
            for iz in range(iz1, iz2):
                b[ix, iz] = 0.5*(a[ix+1, iz] - a[ix-1, iz])

    return b
编译这两个函数时(使用openmp标志),并按如下方式调用它们:

nx, nz = 1024, 1024

a = np.random.rand(nx, nz)
b = np.zeros_like(a)

Nit = 1000
ti = time.time()
for i in range(Nit):
    cytest1(a, b, 5, nx-5, 0, nz)
print('cytest1 : {:.3f} s.'.format(time.time() - ti))

ti = time.time()
for i in range(Nit):
    cytest2(a, b, 5, nx-5, 0, nz)
print('cytest2 : {:.3f} s.'.format(time.time() - ti))
我获得以下执行时间:

cytest1 : 1.757 s.
cytest2 : 1.861 s.
当执行并行函数时,我可以看到我的4个cpu-s在运行,但执行时间几乎与使用串行函数获得的时间相同。我试图将
prange
移动到内部循环,但结果最糟糕。我还尝试了一些不同的
时间表
选项,但没有成功

我显然错过了什么,但是什么呢?
prange
是否无法使用试图访问n+X/n-X元素的代码将循环分块

编辑:

我的设置:

model name      : Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz
MemTotal        : 8052556 kB
Python          : 3.5.2
cython          : 0.28.2
Numpy           : 1.14.2 
Numba           : 0.37.0
setup.py文件:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext


ext_modules = [
    Extension("stencil",
              ["stencil.pyx"],
              libraries=["m"],
              extra_compile_args=["-O3", "-ffast-math", "-march=native", "-fopenmp"],
              extra_link_args=['-fopenmp'],
              )
]

setup(
  name="stencil",
  cmdclass={"build_ext": build_ext},
  ext_modules=ext_modules
)

这个答案将是很多猜测,但正如我们将看到的:很多都取决于硬件,因此如果手头没有相同的硬件,很难解释

第一个问题是:瓶颈是什么?通过查看代码,我假设这是一个内存受限的任务

为了让它更清晰,让我们在循环中只执行以下操作:

 b[ix, iz] = (a[ix+1, iz])
所以没有计算,只有内存访问

我使用Intel Xeon E5-2620@2.1 Ghz,配备2个处理器和
%timeit
-魔术报告:

>>> %timeit cytest1(a,b,5, nx-5, 0, nz)
100 loops, best of 3: 1.99 ms per loop

>>> %timeit cytest2(a,b,5, nx-5, 0, nz)
The slowest run took 234.48 times longer than the fastest. This could mean that an intermediate result is being cached.
1000 loops, best of 3: 324 µs per loop
正如我们所看到的,一些缓存正在进行。我们有2个阵列,每个8Mb—这意味着必须“触摸”16Mb的数据。我机器上的每个处理器都有15Mb的缓存——因此,对于单个线程,数据在可以重用之前会从缓存中移出,但如果同时使用两个处理器,则有20Mb的快速缓存——因此足够大,可以保留所有数据

这意味着我们看到的加速是由于并行化版本可以利用更大数量的快速内存(缓存)

让我们增加阵列的大小,这样即使对于并行化版本,缓存也不够大:

....
>>> nx, nz = 10240, 10240 #100 times bigger
....

>>> %timeit cytest1(a,b,5, nx-5, 0, nz)
1 loop, best of 3: 238 ms per loop

>>> %timeit cytest2(a,b,5, nx-5, 0, nz)
10 loops, best of 3: 99.3 ms per loop
现在它的速度大约快了2倍,这很容易解释:两个处理器的内存带宽是一个处理器的两倍,并且两个处理器都被并行版本利用

对于你的公式,我们得到了非常相似的结果

b[ix, iz] = 0.5*(a[ix+1, iz] - a[ix-1, iz])
这并不奇怪,因为没有足够的计算来限制CPU

sin
cos
是CPU密集型操作,因此使用它们将限制计算CPU(整个代码见附录):

这将产生8的加速,这对于我的机器来说是相当合理的

显然,对于其他机器/架构,可以观察到不同的行为。但简而言之:

  • 我不希望您的公式有太多的速度-任务是内存受限的,所以问题是,您是否可以实现更高的内存访问带宽
  • 对于CPU更密集的计算,您应该能够看到至少一些速度提高,这取决于您的硬件

  • 清单(在windows上,在linux上使用
    -fopenmp
    ):


    这是一个内存受限的任务,因此并行化没有帮助,因为它不会增加内存带宽。更糟糕的是:它有一些开销,这使得它速度变慢,或者可能导致更多的缓存未命中。到目前为止,我的理论。。你可以在你的公式中使用一些像sin或cos这样的cpu密集的东西来测试它,看看在这种情况下并行化是否有一些好处。事实上,在我的机器上,每次代码调用的时间是2.9ms,而不是0.27ms。在核心i7-4771上,单线程方法得到1.6s,多线程方法得到1.0s。(Numba 0.38RC1)。因此,如果您没有最差的处理器或Ram,那么您应该在哪台机器上进行优化?(工作系统/处理器/编译器…)我得到了与Numba和Cython(1.4-1.6 ms/1 ms)完全相同的结果。我使用的是Windows,Python 3.6,MSVCv.1900。@max9111我使用的是Windows,Intel Xeon E5-2620@2.1 Ghz,Python 3.6,Cython 0.27感谢您的分析。我现在明白了!
    ...
    b[ix, iz] = sin(a[ix+1, iz])
    ...
    >>> %timeit cytest1(a,b,5, nx-5, 0, nz)
    1 loop, best of 3: 1.6 s per loop
    
    >>> %timeit cytest2(a,b,5, nx-5, 0, nz)
    1 loop, best of 3: 217 ms per loop
    
    %%cython --compile-args=/openmp --link-args=/openmp 
    from cython.parallel import parallel, prange
    from cython import boundscheck, wraparound
    from libc.math cimport sin
    
    @boundscheck(False)
    @wraparound(False)
    def cytest1(double[:,::1] a, double[:,::1] b, int ix1, int ix2, int iz1, int iz2):
    
        cdef int ix
        cdef int iz
    
        for ix in range(ix1, ix2):
            for iz in range(iz1, iz2):
                b[ix, iz] =sin(a[ix+1, iz])
        return b
    
    
    @boundscheck(False)
    @wraparound(False)
    def cytest2(double[:,::1] a, double[:,::1] b, int ix1, int ix2, int iz1, int iz2):
    
        cdef int ix
        cdef int iz
    
        with nogil, parallel():
            for ix in prange(ix1, ix2):
                for iz in range(iz1, iz2):
                    b[ix, iz] = sin(a[ix+1, iz])
    
        return b