【问题标题】:Cython's prange not improving performanceCython 的计划没有提高性能
【发布时间】:2016-08-19 07:15:34
【问题描述】:

我正在尝试使用 Cython 的 prange 提高一些度量计算的性能。这是我的代码:

def shausdorff(float64_t[:,::1] XA not None, float64_t[:,:,::1] XB not None):
    cdef:
        Py_ssize_t i
        Py_ssize_t n  = XB.shape[2]
        float64_t[::1] hdist = np.zeros(n)

    #arrangement to fix contiguity
    XB = np.asanyarray([np.ascontiguousarray(XB[:,:,i]) for i in range(n)])

    for i in range(n):
        hdist[i] = _hausdorff(XA, XB[i])
    return hdist

def phausdorff(float64_t[:,::1] XA not None, float64_t[:,:,::1] XB not None):
    cdef:
        Py_ssize_t i
        Py_ssize_t n  = XB.shape[2]
        float64_t[::1] hdist = np.zeros(n)

    #arrangement to fix contiguity (EDITED)
    cdef float64_t[:,:,::1] XC = np.asanyarray([np.ascontiguousarray(XB[:,:,i]) for i in range(n)])

    with nogil, parallel(num_threads=4):
        for i in prange(n, schedule='static', chunksize=1):
            hdist[i] = _hausdorff(XA, XC[i])
    return hdist

基本上,在每次迭代中,hausdorff 度量在XA 和每个XB[i] 之间计算。这是_hausdorff函数的签名:

cdef inline float64_t _hausdorff(float64_t[:,::1] XA, float64_t[:,::1] XB) nogil:
    ...

我的问题是顺序的shausdorff 和并行的phausdorff 有相同的时间。此外,phausdorff 似乎根本没有创建任何线程。

所以我的问题是我的代码有什么问题,我该如何修复它以使线程工作。

这是我的setup.py

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

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

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

编辑1:这是cython -a生成的html的链接:custom_metric.html

编辑2:这里是一个如何调用相应函数的例子(你需要先编译the Cython file

import custom_metric as cm
import numpy as np

XA = np.random.random((9000, 210))
XB = np.random.random((1000, 210, 9))

#timing 'parallel' version
%timeit cm.phausdorff(XA, XB)

#timing sequential version
%timeit cm.shausdorff(XA, XB)

【问题讨论】:

  • 您是否尝试过在prange 的循环体中打印与omp_get_thread_num() 等效的内容。见cython.readthedocs.io/en/latest/src/userguide/parallelism.html
  • 会不会是 XB 是一个 Python 对象?使用注释运行cython -a custom_metric.pyx
  • @mavillan 你在使用 OSX 吗?由于我在互联网上看到的信息表明他们提供的 clang 版本不支持 OpenMP。可能请参阅stackoverflow.com/questions/33668323/…,但我自己没有使用 OSX,所以无法确认...
  • @mavillan,你能提供一个关于如何调用shausdorffphausdorff 的小例子吗?
  • 您是否考虑过使用更大的chunk_size?根据算法的不同,使用chunk_size1 的数据局部性可能非常差。

标签: python numpy openmp cython gil


【解决方案1】:

我认为并行化正在工作,但是并行化的额外开销正在消耗它本来可以节省的时间。如果我尝试使用不同大小的数组,那么我确实开始看到并行版本的加速

XA = np.random.random((900, 2100))
XB = np.random.random((100, 2100, 90))

这里并行版本对我来说是串行版本的大约 2/3 时间,这当然不是您期望的 1/4,但至少显示了一些好处。


我可以提供的一个改进是替换修复连续性的代码:

XB = np.asanyarray([np.ascontiguousarray(XB[:,:,i]) for i in range(n)]) 

XB = np.ascontiguousarray(np.transpose(XB,[2,0,1]))

这大大加快了并行和非并行函数的速度(您最初提供的数组是 2 倍)。它确实使您在prange 中的开销减慢了一点点更明显 - 对于您的示例中的数组,串行版本实际上更快。

【讨论】:

  • (作为社区 wiki 发布,因为这没有提供解决方案,所以我想将其从赏金争夺中删除)
猜你喜欢
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多