Je voudrais écrire un programme qui utilise largement les fonctionnalités d'algèbre linéaire de BLAS et LAPACK. Étant donné que la performance est un problème, j'ai fait quelques analyses comparatives et j'aimerais savoir si l'approche que j'ai adoptée est légitime.
J'ai, pour ainsi dire, trois candidats et je veux tester leurs performances avec une simple matrice-matrice multiplication. Les concurrents sont :
- Numpy, utilisant uniquement la fonctionnalité de
point
. - Python, appelant les fonctionnalités de BLAS via un objet partagé.
- C++, appelant les fonctionnalités de BLAS via un objet partagé.
Scénario
J'ai implémenté une multiplication matrice-matrice pour différentes dimensions je
. i
va de 5 à 500 avec un incrément de 5 et les matrices m1
et m2
sont configurées comme ceci :
m1 = numpy.random.rand(i,i).astype(numpy.float32) m2 = numpy.random.rand(i,i).astype(numpy.float32)
1. Numpy
Le code utilisé ressemble à ceci :
tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1 , m2") rNumpy.append((i, tNumpy.repeat(20, 1)))
2. Python, appelant BLAS via un objet partagé
Avec la fonction
_blaslib = ctypes.cdll.LoadLibrary("libblas.so") def Mul(m1, m2, i , r): no_trans = c_char("n") n = c_int(i) un = c_float(1.0) zéro = c_float(0.0) _blaslib.sgemm_(byref(no_trans), byref(no_trans), byref(n), byref (n), byref(n), byref(one), m1.ctypes.data_as(ctypes.c_void_p), byref(n), m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(zero) , r.ctypes.data_as(ctypes.c_void_p), byref(n))
le code de test ressemble à ceci :
r = numpy.zeros ((i,i), numpy.float32) tBlas = timeit.Timer("Mul(m1, m2, i, r)", "import numpy; from __main__ import i, m1, m2, r, Mul") rBlas. ajouter((i, tBlas.repeat(20, 1)))
3. c++, appelant BLAS via un objet partagé
Maintenant, le code c++ est naturellement un peu plus long donc je réduis les informations au minimum.
Je charge la fonction avec
void* handle = dlopen("libblas.so", RTLD_LAZY); void* Func = dlsym(handle, "sgemm_");
Je mesure le temps avec gettimeofday
comme ceci :
gettimeofday(&start, NULL); f(&no_trans, &no_trans, &dim, &dim, &dim, &one, A, &dim, B, &dim, &zero, Return, &dim); gettimeofday(&end, NULL); dTimes[j] = CalcTime(start, end);
où j
est une boucle exécutée 20 fois. Je calcule le temps passé avec
double CalcTime(timeval start, timeval end) { double factor = 1000000; retour (((double)end.tv_sec) * facteur + ((double)end.tv_usec) - (((double)start.tv_sec) * facteur + ((double)start.tv_usec))) / facteur ; }
Résultats
Le résultat est affiché dans le graphique ci-dessous :
Questions
- Pensez-vous que mon approche est juste, ou y a-t-il des frais généraux inutiles que je peux éviter ?
- Vous attendriez-vous à ce que le résultat montre un écart aussi énorme entre l'approche c++ et python ? Les deux utilisent des objets partagés pour leurs calculs.
- Puisque je préférerais utiliser python pour mon programme, que pourrais-je faire pour augmenter les performances lors de l'appel des routines BLAS ou LAPACK ?
Télécharger
Le benchmark complet peut être téléchargé ici. (JF Sebastian a rendu ce lien possible^^)