我需要快速計算一個矩陣,其條目是通過用每個行的向量卷積濾波器獲得的,對結果向量的條目進行二次取樣,然後將結果的點積與另一個向量。具體而言,我想計算意想不到的慢的cython卷積代碼
M = [CONV(e_j,F)* P_I * V_I] _ {I,J},
其中i從1變化到n和j從1到m而變化。這裏e_j是大小爲n的指標(行)向量,其中只有列j中的一個,f是長度爲s的濾波器,P_i是(n + s-1)個由k個矩陣,卷積,並且v_i是長度爲k的列向量。對於n = 6,m = 7,s = 3,則需要O(n * s)個操作來計算M的每個條目,因此O(n * s * n * m)我的電腦核心(8GLOPs)應該能夠在0.94微秒內計算出M值。然而,我的非常簡單的cython實現,在example given in the Cython documentation之後,需要超過2毫秒才能用這些參數計算一個例子。這差不多有4個數量級!
這是一個使用Cython實現和測試代碼的shar文件。將其複製並粘貼到一個文件中,然後在乾淨的目錄中運行'bash <fname>'以獲取代碼,然後運行'bash ./test.sh'以查看糟糕的性能。
cat > fastcalcM.pyx <<'EOF'
import numpy as np
cimport numpy as np
cimport cython
from scipy.signal import convolve
DTYPE=np.float32
ctypedef np.float32_t DTYPE_t
@cython.boundscheck(False)
def calcM(np.ndarray[DTYPE_t, ndim=1, negative_indices=False] filtertaps, int
n, int m, np.ndarray[np.int_t, ndim=2, negative_indices=False]
keep_indices, np.ndarray[DTYPE_t, ndim=2, negative_indices=False] V):
""" Computes a numrows-by-k matrix M whose entries satisfy
M_{i,k} = [conv(e_j, f)^T * P_i * v_i],
where v_i^T is the i-th row of V, and P_i samples the entries from
conv(e_j, f)^T indicated by the ith row of the keep_indices matrix """
cdef int k = keep_indices.shape[1]
cdef np.ndarray M = np.zeros((n, m), dtype=DTYPE)
cdef np.ndarray ej = np.zeros((m,), dtype=DTYPE)
cdef np.ndarray convolution
cdef int rowidx, colidx, kidx
for rowidx in range(n):
for colidx in range(m):
ej[colidx] = 1
convolution = convolve(ej, filtertaps, mode='full')
for kidx in range(k):
M[rowidx, colidx] += convolution[keep_indices[rowidx, kidx]] * V[rowidx, kidx]
ej[colidx] = 0
return M
EOF
#-----------------------------------------------------------------------------
cat > test_calcM.py << 'EOF'
import numpy as np
from fastcalcM import calcM
filtertaps = np.array([-1, 2, -1]).astype(np.float32)
n, m = 6, 7
keep_indices = np.array([[1, 3],
[4, 5],
[2, 2],
[5, 5],
[3, 4],
[4, 5]]).astype(np.int)
V = np.random.random_integers(-5, 5, size=(6, 2)).astype(np.float32)
print calcM(filtertaps, n, m, keep_indices, V)
EOF
#-----------------------------------------------------------------------------
cat > test.sh << 'EOF'
python setup.py build_ext --inplace
echo -e "%run test_calcM\n%timeit calcM(filtertaps, n, m, keep_indices, V)" > script.ipy
ipython script.ipy
EOF
#-----------------------------------------------------------------------------
cat > setup.py << 'EOF'
from distutils.core import setup
from Cython.Build import cythonize
import numpy
setup(
name="Fast convolutions",
include_dirs = [numpy.get_include()],
ext_modules = cythonize("fastcalcM.pyx")
)
EOF
我想也許調用SciPy的的卷積可能是罪魁禍首(我不能肯定,用Cython和SciPy的發揮得很好),所以我實現我自己的卷積碼ALA用Cython文件中相同的例子,但這導致整體代碼慢10倍左右。
關於如何接近理論上可能的速度的任何想法,或差異如此之大的原因?
感謝您的細節!我不明白關於轉換類型或內存視圖語法的一點,但是一般來說,這聽起來很重要且有用;我會查找它們,實施你的建議,並可能最終接受這個答案。順便說一句,你會如何改善時間估計?任何指導方針都會有很大的幫助:我試圖養成一個習慣,弄清楚我的代碼和最佳可能之間的差距。我能想到的唯一被省略的因素是內存訪問成本(這可能會僞造我的估計)。 – AatG
你通常不能只是猜測那樣的時間。首先,「O(nm)」與「nm」不同。另外,操作速度也有很大的不同,而且大量的開銷是不可避免的。我的猜測是'_correlateND'交易速度的準確性。你總是可以嘗試自己實現它(或從頭開始)。 – Veedrac