python - Cython 比纯 Python 稍快或稍慢

标签 python python-2.7 numpy scipy cython

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

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

  1. 纯 python(循环 2D Python 列表)
  2. NumPy(二维 NumPy 数组的点积)
  3. Weave inline(C++ 在二维数组上循环)
  4. Cython(循环 2D Python 列表 + 静态类型)
  5. Cython-Numpy(循环二维 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 的性能让我哭了。我的整个代码分为两个文件。一切都是自动化的,您只需运行第一个文件即可查看所有结果。有人可以通过指出我可以做些什么来获得更好的结果来帮助我吗?

ma​​tmul.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]

这就是 Python 中 numpy 数组的索引方式,也是 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/

相关文章:

python - 无法使用我创建的用户登录 django 的管理页面

python - 使用 Cython 的 bool numpy 数组

python - Pandas 多重索引在给定元组时成功,在给定列表时失败

python-2.7 - 使用 celery 构建微服务

python - 两个二维列表的逐元素乘积

python - 类型错误 : 'int' object is not callable python 2. 7.5

Python - 用指数函数拟合数据

python - 使用嵌套列表计算足球队获胜的次数

python - 使用旋转的垂直条形图 ='vertical' 不起作用

python - 使用 Python (python-pptx) 编辑底层 PowerPoint XML