Ich versuche derzeit grundlegende Matrix Vektor Multiplikation in Cython zu implementieren (als Teil einer viel larger project to reduce computation) und feststellen, dass mein Code etwa 2x langsamer als Numpy.dot
ist.Was verursacht die 2x Verlangsamung in meiner Cython-Implementierung der Matrix-Vektor-Multiplikation?
Ich frage mich, ob es etwas gibt, das ich vermisse, das in der Verlangsamung resultiert. Ich schreibe optimierten Cython-Code, deklariere Variablentypen, benötige zusammenhängende Arrays und vermeide Cache-Misses. Ich habe sogar versucht, Cython als Wrapper zu verwenden und nativen C-Code aufzurufen (siehe unten).
Ich frage mich: was sonst könnte ich tun, um meine Implementierung zu beschleunigen, so läuft so schnell wie NumPy für diese grundlegende Operation?
Der Cython Code, ich verwende ist Beow:
import numpy as np
cimport numpy as np
cimport cython
DTYPE = np.float64;
ctypedef np.float64_t DTYPE_T
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def matrix_vector_multiplication(np.ndarray[DTYPE_T, ndim=2] A, np.ndarray[DTYPE_T, ndim=1] x):
cdef Py_ssize_t i, j
cdef Py_ssize_t N = A.shape[0]
cdef Py_ssize_t D = A.shape[1]
cdef np.ndarray[DTYPE_T, ndim=1] y = np.empty(N, dtype = DTYPE)
cdef DTYPE_T val
for i in range(N):
val = 0.0
for j in range(D):
val += A[i,j] * x[j]
y[i] = val
return y
ich diese Datei bin Kompilieren (seMatrixVectorExample.pyx
) das folgende Skript:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np
ext_modules=[ Extension("seMatrixVectorExample",
["seMatrixVectorExample.pyx"],
libraries=["m"],
extra_compile_args = ["-ffast-math"])]
setup(
name = "seMatrixVectorExample",
cmdclass = {"build_ext": build_ext},
include_dirs = [np.get_include()],
ext_modules = ext_modules
)
und mit dem folgenden Testskript zur Beurteilung der Leistung:
import numpy as np
from seMatrixVectorExample import matrix_vector_multiplication
import time
n_rows, n_cols = 1e6, 100
np.random.seed(seed = 0)
#initialize data matrix X and label vector Y
A = np.random.random(size=(n_rows, n_cols))
np.require(A, requirements = ['C'])
x = np.random.random(size=n_cols)
x = np.require(x, requirements = ['C'])
start_time = time.time()
scores = matrix_vector_multiplication(A, x)
print "cython runtime = %1.5f seconds" % (time.time() - start_time)
start_time = time.time()
py_scores = np.exp(A.dot(x))
print "numpy runtime = %1.5f seconds" % (time.time() - start_time)
Für eine Testmatrix mit n_rows = 10e6
und n_cols = 100
ich:
cython runtime = 0.08852 seconds
numpy runtime = 0.04372 seconds
Edit: Es ist erwähnenswert, dass die Verlangsamung bleibt auch, wenn ich die Matrixmultiplikation in nativen C-Code implementieren, und nur Gebrauch Cython als Wrapper.
void c_matrix_vector_multiplication(double* y, double* A, double* x, int N, int D) {
int i, j;
int index = 0;
double val;
for (i = 0; i < N; i++) {
val = 0.0;
for (j = 0; j < D; j++) {
val = val + A[index] * x[j];
index++;
}
y[i] = val;
}
return;
}
und hier ist die Cython Wrapper, der nur den Zeiger auf das erste Element sendet von y
, A
und x
. :
import cython
import numpy as np
cimport numpy as np
DTYPE = np.float64;
ctypedef np.float64_t DTYPE_T
# declare the interface to the C code
cdef extern void c_multiply (double* y, double* A, double* x, int N, int D)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def multiply(np.ndarray[DTYPE_T, ndim=2, mode="c"] A, np.ndarray[DTYPE_T, ndim=1, mode="c"] x):
cdef int N = A.shape[0]
cdef int D = A.shape[1]
cdef np.ndarray[DTYPE_T, ndim=1, mode = "c"] y = np.empty(N, dtype = DTYPE)
c_multiply (&y[0], &A[0,0], &x[0], N, D)
return y
[Dies] (http://stackoverflow.com/questions/10442365/why-is-matrix-multiplica-faster-with-numpy-than-with-ctypes-in-python) Frage/Antwort scheint verwandt, mit verschiedenen Gründen in der oberen Antwort gegeben. Hör zu. – russianfool
@russianfool Danke! Ich habe die Antworten auf diese Frage tatsächlich durchgelesen, aber die angegebenen Gründe sind für dieses Problem nicht besonders relevant, da es sich um eine Matrix-Vektor-Multiplikation anstelle einer Matrix-Matrix-Multiplikation handelt. Ich werde das in meiner Frage klären. –
Scheint etwas mit mir verwandt; Lies nämlich die Bits über BLAS/abgerollte Schleifen. Sie können eine Matrix-Vektor-Multiplikationsimplementierung finden [http://www.netlib.org/clapack/cblas/cgemv.c], und es sieht definitiv so aus, als hätten sie verschiedene optimierte Versionen basierend auf Ihren Daten Passing in. Nebenbei bemerkt, ich bin nicht vertraut mit Distutils ... Könnten Sie in -O2 als eine der Extra_Compile_args übergeben? Wenn es nicht mit -O2 unter der Haube kompiliert wird, macht es keinen Sinn, die Leistung zu vergleichen. – russianfool