¿Qué está causando la ralentización 2x en mi implementación de Cython de la multiplicación de vectores de matriz?

Actualmente estoy tratando de implementar la multiplicación básica de vectores matriciales en Cython (como parte de muchoproyecto más grande para reducir la computación) y descubro que mi código es aproximadamente 2 veces más lento queNumpy.dot.

Me pregunto si hay algo que me falta y que está provocando la desaceleración. Estoy escribiendo código optimizado de Cython, declarando tipos de variables, requiriendo matrices contiguas y evitando errores de caché. Incluso intenté tener Cython como envoltorio y llamar al código C nativo (ver más abajo).

Me pregunto:¿Qué más podría hacer para acelerar mi implementación para que se ejecute tan rápido como NumPy para esta operación básica?

El código de Cython que estoy usando es 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

Estoy compilando este archivo (seMatrixVectorExample.pyx) utilizando el siguiente script:

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
)

y usando el siguiente script de prueba para evaluar el rendimiento:

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)

Para una matriz de prueba conn_rows = 10e6 yn_cols = 100 Yo obtengo:

cython runtime = 0.08852 seconds
numpy runtime = 0.04372 seconds

Editar: Vale la pena mencionar que la desaceleración persiste incluso cuando implemento la multiplicación de matrices en código C nativo, y solo uso Cython como envoltorio.

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; 
}

y aquí está el contenedor de Cython, que solo envía el puntero al primer elemento dey, A yx. :

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

Respuestas a la pregunta(1)

Su respuesta a la pregunta