[PYTHON] Benchmark par produit matriciel: NumPy, Numba, Cython, Swig, OpenCL, intelMKL

Je souhaite intégrer l'apprentissage profond dans un environnement de classe intégré et je recherche une méthode facile à implémenter et rapide à exécuter. Donc, pour un petit benchmark, j'ai fait calculer le produit de deux matrices ** NxN ** en NumPy, Numba, Cython, Swig, OpenCL et intelMKL.

Swig a implicitement lancé le flotteur pour doubler à l'intérieur, et certains des mémos en cours de confirmation d'implémentation sont devenus des zombies, mais veuillez l'accepter. J'attendrai PL en raison de l'augmentation du niveau de comparaison et de la différence d'environnement.

NumPyCuPy_test/Test01.ipynb at master · Chachay/NumPyCuPy_test

La personne à multiplier cette fois

Matrice NxN simple précision ma Identique à la matrice NxN simple précision mb

Définition de la matrice


N = 1000 #Correspond à N dans le tableau ci-dessous
# Generate NxN randomized matrix
ma = np.random.rand(N, N).astype(np.float32)
mb = np.random.rand(N, N).astype(np.float32)

Sommaire des résultats

Le résultat de l'utilisation du point de Numpy dans Numba est écrasant. Mais le résultat de l'exécution n'est-il pas mis en cache?

N=3 N=100 N=1000
Temps d'exécution[us] la vitesse Temps d'exécution[us] la vitesse Temps d'exécution[ms] la vitesse
1 Numpy Native 1.34 1.00 94.2 1.00 87.9 1.00
2 Numba Simple Mult 1.24 1.08 1,040.0 0.09 6,870.0 0.01
3 Numba Numpy 2.03 0.66 74.6 1.26 23.5 3.74
4 Cython Simple Mult 19.20 0.07 660,000.0 0.00 NA -
5 Cython Numpy 1.52 0.88 94.1 1.00 87.8 1.00
6 Swig Simple Mult 1.97 0.68 837.0 0.11 7,450.0 0.01
7 Swig MKL cblas_dgemm 2.57 0.52 123.0 0.77 95.8 0.92
8 OpenCL Simple Mult(Parallel) 2,560.00 0.00 3,160.0 0.03 877.0 0.10

[Mémo] J'aurais dû faire une note lors de son exécution, mais la relation de taille est étrange. Je vais réessayer.

Numpy

Voir référence

np.dot(ma, mb)

Numba

Préparez Num_NpDot, qui est np.dot multiplié par jit, et Num_Dot, qui effectue la multiplication matricielle telle que définie en mathématiques.

from numba import jit
@jit
def Num_NpDot(a, b):
    return np.dot(a, b)
@jit    
def Num_Dot(a, b):
    c = np.zeros((a.shape[0], b.shape[1]))
    for i in range(a.shape[0]):
        for j in range(b.shape[1]):
            for k in range(a.shape[1]):
                c[i][j] += a[i][k] * b[k][j]
    return c

Cython

Je pensais pouvoir battre Numba parce que j'en prenais soin tout en faisant attention à définir le type, et à le traduire en C, mais le résultat était comme cette fois, et j'ai été assez déçu de toi.

Identique au niveau Numba. La construction est (devrait) être une optimisation équivalente à O2 dans VC2015.

cimport numpy as np
cimport cython
import numpy as np

@cython.boundscheck(False) # turn off bounds-checking for entire function
@cython.wraparound(False)  # turn off negative index wrapping for entire function

cpdef np.ndarray Cy_NpDot(np.ndarray a, np.ndarray b):
    return np.dot(a, b)
    
cpdef np.ndarray Cy_Dot(np.ndarray a, np.ndarray b):
    cdef np.ndarray c
    c = np.zeros((a.shape[0], b.shape[0]))
    for i in range(a.shape[0]):
        for j in range(b.shape[1]):
            for k in range(a.shape[1]):
                c[i][j] += a[i][k] * b[k][j]
    return c

J'avais des ennuis parce que Cy_Dot est devenu un enfant très inutilisable même s'il était à travers Cython.

Swig

Je vous suis redevable récemment. Je fais du Python, mais j'écris des points importants en C ++. Python n'est utilisé que pour la gestion des données, la méthode.

Comme Numba, Intel MKL a grimpé comme un niveau équivalent à la soi-disant multiplication Swig_Dot et Numpy. La version est une optimisation équivalente à VC2015 et O2 (devrait l'être).

Bien que ce soit simple, je l'ai préparé en double. Oups. (Surtout cela semble étrange dans MKL, mais je ferme les yeux pour le moment)

void Swig_Dot(int mm1, int mn1, double* mat1,
              int mm2, int mn2, double* mat2,
              double** outmat, int* mm, int* mn)
{
    double* arr = NULL;
    arr = (double*)calloc(mm1 * mn2, sizeof(double));
    
    // Multiplying matrix a and b and storing in array mult.
    for(int i = 0; i < mm1; ++i)
        for(int j = 0; j < mn2; ++j)
            for(int k = 0; k < mn1; ++k)
                arr[i*mn2+j] += mat1[i*mn1+k] * mat2[k*mn2+j];
    *mm = mm1;
    *mn = mn2;
    *outmat = arr;
}

void Swig_Dot_MKL(int mm1, int mn1, double* mat1,
              int mm2, int mn2, double* mat2,
              double** outmat, int* mm, int* mn)
{
    double alpha = 1.0;
    double beta = 0.0;

    double *C = (double *)mkl_malloc( mm1*mn2*sizeof( double ), 64 );
    if (C == NULL) {
        printf( "\n ERROR: Can't allocate memory for matrices. Aborting... \n\n");
        mkl_free(C);
        return;
    }

    for (int i = 0; i < (mm1*mn2); i++) {
        C[i] = 0.0;
    }
    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 
                mm1, mn2, mn1, alpha, mat1, mn1, mat2, mn2, beta, C, mn2);

    double *res = (double *)malloc( mm1*mn2*sizeof( double ));
    memcpy(res, C, mm1*mn2*sizeof( double ));
    *mm = mm1;
    *mn = mn2;
    *outmat = res;
    mkl_free(C);
}

OpenCL

prime. Considérez-le comme un stub pour la comparaison avec le GPU. Je voulais le comparer avec CuPy, mais il semble que ce soit juste un club macho matériel.

réduction.

Les références

Recommended Posts

Benchmark par produit matriciel: NumPy, Numba, Cython, Swig, OpenCL, intelMKL
Visualisation de la matrice créée par numpy
[Calcul scientifique / technique par Python] Calcul du produit de la matrice par l'opérateur @, python3.5 ou supérieur, numpy
Gérer numpy avec Cython (méthode par memoryview)