簡體   English   中英

Cython比純Python快或慢

[英]Cython either marginally faster or slower than pure Python

我正在使用幾種技術( NumPyWeaveCython )執行Python性能基准測試。 代碼基本上在數學上所做的是C = AB ,其中A,B和C是N x N矩陣( 注意:這是矩陣乘積,而不是逐元素乘法)。

我已經編寫了5種不同的代碼實現:

  1. 純Python(在2D Python列表中循環)
  2. NumPy(二維NumPy陣列的點積)
  3. 內聯編織(C ++遍歷2D數組)
  4. Cython(在2D Python列表上循環+靜態鍵入)
  5. 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]

這就是在python中索引numpy數組的方式,這就是Cython優化的語法。 進行此更改后,您應該獲得不錯的性能。

Cython的注釋功能確實有助於發現此類優化問題。 您可能會注意到A[i][j]會產生大量的Python API調用,而A[i,j]不會產生任何調用。

另外,如果您手動初始化所​​有條目,則np.emptynp.zeros更合適。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM