我正在使用几种技术( NumPy Weave Cython )来执行 Python 性能基准测试。代码在数学上基本上做的是 C = AB ,其中 A、B 和 C 是 N x N 矩阵( 注意: 这是矩阵乘积而不是元素乘法)。

我已经编写了 5 种不同的代码实现:

  • 纯 python(循环 2D Python 列表)
  • NumPy(二维 NumPy 数组的点积)
  • Weave 内联(二维数组上的 C++ 循环)
  • Cython(循环 2D Python 列表 + 静态类型)
  • Cython-Numpy(循环 2D NumPy 数组 + 静态类型)

  • 我的期望是实现 2 到 5 将比实现 1 快得多。然而,我的结果表明并非如此。这些是我相对于纯 Python 实现的标准化加速结果:
  • python_list: 1.00
  • numpy_array:330.09
  • weave_inline: 30.72
  • cython_list: 2.80
  • cython_array: 0.14

  • 我对 NumPy 的表现很满意,但我对 Weave 的表现不太热情,Cython 的表现让我哭了。我的整个代码分为两个文件。一切都是自动化的,您只需要运行第一个文件即可查看所有结果。有人可以通过指示我可以做些什么来获得更好的结果来帮助我吗?

    matmul.py:
    import time
    
    import numpy as np
    from scipy import weave
    from scipy.weave import converters
    
    import pyximport
    pyximport.install()
    import cython_matmul as cml
    
    
    def python_list_matmul(A, B):
        C = np.zeros(A.shape, dtype=float).tolist()
        A = A.tolist()
        B = B.tolist()
        for k in xrange(len(A)):
            for i in xrange(len(A)):
                for j in xrange(len(A)):
                    C[i][k] += A[i][j] * B[j][k]
        return C
    
    
    def numpy_array_matmul(A, B):
        return np.dot(A, B)
    
    
    def weave_inline_matmul(A, B):
        code = """
           int i, j, k;
           for (k = 0; k < N; ++k)
           {
               for (i = 0; i < N; ++i)
               {
                   for (j = 0; j < N; ++j)
                   {
                       C(i, k) += A(i, j) * B(j, k);
                   }
               }
           }
           """
    
        C = np.zeros(A.shape, dtype=float)
        weave.inline(code, ['A', 'B', 'C', 'N'], type_converters=converters.blitz, compiler='gcc')
        return C
    
    
    N = 100
    A = np.random.rand(N, N)
    B = np.random.rand(N, N)
    
    function = []
    function.append([python_list_matmul, 'python_list'])
    function.append([numpy_array_matmul, 'numpy_array'])
    function.append([weave_inline_matmul, 'weave_inline'])
    function.append([cml.cython_list_matmul, 'cython_list'])
    function.append([cml.cython_array_matmul, 'cython_array'])
    
    t = []
    for i in xrange(len(function)):
        t1 = time.time()
        C = function[i][0](A, B)
        t2 = time.time()
        t.append(t2 - t1)
        print function[i][1] + ' \t: ' + '{:10.6f}'.format(t[0] / t[-1])
    

    cython_matmul.pyx:
    import numpy as np
    cimport numpy as np
    
    import cython
    cimport cython
    
    DTYPE = np.float
    ctypedef np.float_t DTYPE_t
    
    
    @cython.boundscheck(False)
    @cython.wraparound(False)
    @cython.nonecheck(False)
    cpdef cython_list_matmul(A, B):
    
        cdef int i, j, k
        cdef int N = len(A)
    
        A = A.tolist()
        B = B.tolist()
        C = np.zeros([N, N]).tolist()
    
        for k in xrange(N):
            for i in xrange(N):
                for j in xrange(N):
                    C[i][k] += A[i][j] * B[j][k]
        return C
    
    
    @cython.boundscheck(False)
    @cython.wraparound(False)
    @cython.nonecheck(False)
    cpdef cython_array_matmul(np.ndarray[DTYPE_t, ndim=2] A, np.ndarray[DTYPE_t, ndim=2] B):
    
        cdef int i, j, k, N = A.shape[0]
        cdef np.ndarray[DTYPE_t, ndim=2] C = np.zeros([N, N], dtype=DTYPE)
    
        for k in xrange(N):
            for i in xrange(N):
                for j in xrange(N):
                    C[i][k] += A[i][j] * B[j][k]
        return C
    

    最佳答案

    Python 列表和高性能数学不兼容,忘记 cython_list_matmul

    您的 cython_array_matmul 唯一的问题是索引使用不正确。它应该是

    C[i,k] += A[i,j] * B[j,k]
    

    这就是 numpy 数组在 Python 中的索引方式,也是 Cython 优化的语法。
    通过此更改,您应该获得不错的性能。

    Cython 的 annotation feature 非常有助于发现这样的优化问题。您可能会注意到 A[i][j] 会产生大量 Python API 调用,而 A[i,j] 不会产生任何调用。

    此外,如果您手动初始化所​​有条目,则 np.emptynp.zeros 更合适。

    关于python - Cython 比纯 Python 稍微快一点或慢一点,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17027487/

    10-12 23:23