Vorrei scrivere un programma che faccia un uso estensivo delle funzionalità di algebra lineare BLAS e LAPACK. Poiché le prestazioni sono un problema, ho eseguito alcuni benchmark e vorrei sapere se l'approccio che ho adottato è legittimo.
Ho, per così dire, tre concorrenti e voglio testare le loro prestazioni con una semplice moltiplicazione matrice-matrice. I concorrenti sono:
- Numpy, facendo uso solo della funzionalità di
dot
. - Python, chiamando le funzionalità BLAS tramite un oggetto condiviso.
- C ++, chiamando le funzionalità BLAS tramite un oggetto condiviso.
Scenario
Ho implementato una moltiplicazione matrice-matrice per diverse dimensioni i
. i
va da 5 a 500 con un incremento di 5 e le matrici m1
e m2
si configurano così:
m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)
1. Numpy
Il codice utilizzato è simile a questo:
tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))
2. Python, chiamando BLAS tramite un oggetto condiviso
Con la funzione
_blaslib = ctypes.cdll.LoadLibrary("libblas.so")
def Mul(m1, m2, i, r):
no_trans = c_char("n")
n = c_int(i)
one = c_float(1.0)
zero = 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))
il codice di prova ha questo aspetto:
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.append((i, tBlas.repeat(20, 1)))
3. c ++, chiamando BLAS tramite un oggetto condiviso
Ora il codice c ++ è naturalmente un po 'più lungo, quindi riduco le informazioni al minimo.
Carico la funzione con
void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
Misuro il tempo in gettimeofday
questo modo:
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);
dove j
è un ciclo in esecuzione 20 volte. Calcolo il tempo trascorso con
double CalcTime(timeval start, timeval end)
{
double factor = 1000000;
return (((double)end.tv_sec) * factor + ((double)end.tv_usec) - (((double)start.tv_sec) * factor + ((double)start.tv_usec))) / factor;
}
risultati
Il risultato è mostrato nella trama sottostante:
Domande
- Pensi che il mio approccio sia giusto o ci sono delle spese generali non necessarie che posso evitare?
- Ti aspetteresti che il risultato mostrasse un'enorme discrepanza tra l'approccio c ++ e quello python? Entrambi utilizzano oggetti condivisi per i loro calcoli.
- Dato che preferirei usare python per il mio programma, cosa potrei fare per aumentare le prestazioni quando chiamo routine BLAS o LAPACK?
Scarica
Il benchmark completo può essere scaricato qui . (JF Sebastian ha reso possibile quel collegamento ^^)
r
matrice è ingiusta. Sto risolvendo il "problema" in questo momento e posto i nuovi risultati.
np.ascontiguousarray()
(considera l'ordine C rispetto a Fortran). 2. assicurati che np.dot()
usi lo stesso libblas.so
.
m1
e m2
hanno il ascontiguousarray
flag come True
. E numpy usa lo stesso oggetto condiviso di C. Per quanto riguarda l'ordine dell'array: Attualmente non sono interessato al risultato del calcolo, quindi l'ordine è irrilevante.