2012-06-29 16 views
8

Sto usando OpenCV per un'applicazione in computer vision. Vorrei accelerare alcune operazioni con le matrici (le matrici sono piuttosto grandi) sulla GPU e voglio evitare di scrivere codice direttamente in CUDA C, se possibile. OpenCV 2.4.1 ha un numero di funzioni accelerate GPU. Quanto bene si esibiscono nella tua esperienza? Sto meglio usando un'altra libreria (ad esempio Thrust)?Quanto è buona la libreria GPU OpenCV per le operazioni con le matrici?

MODIFICA Esempio di applicazione: Calculate squared Euclidean distance matrix on GPU. Attualmente, l'implementazione accelerata (e vettorializzata) della mia GPU in Matlab utilizzando Parallel Computing Toolbox (PCT) è circa 5-10 volte più veloce della mia implementazione in C++ con OpenCV.

implementazione Matlab:

function K = sqEuclideanDist(P_cpu,Q_cpu) 
% Vectorized method to compute pairwise squared Euclidean distance on GPU 
% Returns K(i,j) = (P(i,:) - Q(j,:))'*(P(i,:) - Q(j,:)) 

P_gpu = gpuArray(P_cpu); 
Q_gpu = gpuArray(Q_cpu); 

[nP, d] = size(P_gpu); 
[nQ, d] = size(Q_gpu); 

pmag = sum(P_gpu .* P_gpu, 2); 
qmag = sum(Q_gpu .* Q_gpu, 2); 

% note that K is on GPU 
K = ones(nP,1)*qmag' + pmag*ones(1,nQ) - 2*P_gpu*Q_gpu'; 

end 

UPDATE Ecco un'altra implementazione Matlab che compie gli stessi (grazie a https://stackoverflow.com/a/7774323/1121420). Ma funziona solo su CPU perché bsxfun non è supportato da PCT. Ancora cercando l'alternativa C++ però.

function K = sqEuclideanDist(P_cpu,Q_cpu) 
% Returns K(i,j) = (P(i,:) - Q(j,:))'*(P(i,:) - Q(j,:)) 
% Runs on CPU only. 

K = bsxfun(@plus,sum(p.^2,2),sum(q.^2,2)') - 2*(p*q'); 

end 
+0

Quali funzioni, in particolare, si stanno prendendo in considerazione utilizzando? –

+0

Materie di base. gpu :: reduce, gpu :: multiply (per moltiplicazione di matrice di elementi). Inoltre, moltiplicazione di matrici, individuazione di autovalori di matrice e autovettori, trasposizione di matrici. – Alexey

+1

@Alex - tutte le semplici matrici utilizzano direttamente la libreria NVidia (thrust?) E quindi sono ottimizzate ottimamente –

risposta

3

Trovo che lo ArrayFire sia molto più veloce e che abbia iniziato ad usarlo al posto dei kernel GPU in OpenCV per l'elaborazione delle immagini. Qui ci sono some benchmarks Ho trovato il confronto tra ArrayFire (utilizzato in un'interfaccia diversa chiamata LibJacket) in OpenCV ed è stato vero anche nel mio benchmarking che ArrayFire è 2-4X più veloce delle funzioni GPU in OpenCV. Da quello che ho sentito, NVIDIA non ha scritto i kernel GPU in OpenCV ma li ha commissionati a qualcuno, il che potrebbe essere il motivo per cui sono così lenti. Dal momento che sto usando solo 1 GPU, posso usare ArrayFire gratuitamente.

Aggiornamento, dato il nuovo codice MATLAB pubblicato da @Alex: Ho eseguito il benchmark di questo codice sul mio sistema. Ho capito che il Parallel Computing Toolbox gpuArray è più lento della CPU, ma Jacket e ArrayFire. spec HW sono:

Intel(R) Xeon(R) CPU X5660 @ 2.80GHz 
NVIDIA Tesla M2090 

Risultati della CPU vs GPU utilizzando Parallel Computing Toolbox gpuArray (completamente riscaldato). CPU è più veloce di PCT gpuArray:

>> tic; sqEuclideanDist(gpuArray(rand(1581,3)),gpuArray(rand(189,3))); toc; 
Elapsed time is 0.006859 seconds. 
>> tic; sqEuclideanDist(rand(1581,3),rand(189,3)); toc; 
Elapsed time is 0.005712 seconds. 

Risultati della CPU vs GPU utilizzando Jacket (completamente riscaldato). Jacket batte PCT gpuArray di 3.7X e batte la CPU da 3X

>> tic; sqEuclideanDist(gdouble(rand(1581,3)),gdouble(rand(189,3))); toc; 
Elapsed time is 0.001876 seconds. 

Ecco il codice modificato che ti permette di eseguire tutti così facilmente:

function K = sqEuclideanDist(P,Q) 
% Vectorized method to compute pairwise squared Euclidean distance on GPU 
% Returns K(i,j) = (P(i,:) - Q(j,:))'*(P(i,:) - Q(j,:)) 

[nP, d] = size(P); 
[nQ, d] = size(Q); 

pmag = sum(P .* P, 2); 
qmag = sum(Q .* Q, 2); 

K = ones(nP,1)*qmag' + pmag*ones(1,nQ) - 2*P*Q'; 

end 

Giacca fa supporto BSXFUN sulla GPU, e lo fa migliorare le velocità di un po ' :

>> tic; sqEuclideanDist(gdouble(rand(1581,3)),gdouble(rand(189,3))); toc; 
Elapsed time is 0.001420 seconds. 

Nota che le dimensioni utilizzate qui sono piuttosto piccole, quindi la maggior parte del codice CUDA che tenta di funzionare su queste piccole dimensioni è probabile che scarso rendimento. È per questo che mi piace usare le cose di AccelerEyes, perché quei ragazzi hanno ottimizzato la GPU, a differenza di PCT gpuArray, Thrust, OpenCV, ognuno dei quali ho provato in passato.

Ecco l'ArrayFire libero C++ risultati:

Time: 0.0003577 seconds 
Speedups: 19.2X faster than PCT gpuArray, 16X faster than the CPU, 5.2X faster 
than Jacket in MATLAB original version, 4X faster than Jacket in MATLAB using 
BSXFUN 

Qui è il codice ArrayFire che ho scritto per questo:

static array SqEuclideanDist(array P, array Q) 
{ 
    // 0 based indexing 
    array pmag = sum(P * P, 1); 
    array qmag = sum(Q * Q, 1); 

    int np = P.dims(0); 
    int nq = Q.dims(0); 

    array K = tile(qmag.T(), np, 1) * tile(pmag, 1, nq) - 2 * matmul(P, Q.T()); 
    return K; 
} 

int main(int argc, char **argv) 
{ 
    double *P_cpu = new double[1581 * 3]; 
    double *Q_cpu = new double[189 * 3]; 

    array P = array(1581, 3, P_cpu); 
    array Q = array(189 , 3, Q_cpu); 
    af::sync(); 

    int iter = 1000; 

    timer::tic(); 
    for (int i = 0; i < iter; i++) { 
     array K = SqEuclideanDist(P, Q); 
     af::eval(K); 
    } 

    af::sync(); 
    printf("Time taken: %2.4lfms\n", (1000 * timer::toc())/iter); 

    delete[] P_cpu; 
    delete[] Q_cpu; 
} 
+1

ottimo lavoro. Grazie per aver fornito le alternative. Sicuramente ho imparato qualcosa oggi: non conoscevo il supporto di Jacket per bsxfun e mi piace il semplice codice di ArrayFire. L'unica cosa è - anche se esiste una versione gratuita della libreria ArrayFire C++, la versione gratuita offre funzionalità piuttosto limitate (ad esempio non supporta le operazioni di algebra lineare). Sto cercando una libreria open source, puoi suggerirne qualcuno? – Alexey

+0

Prego. Sorprendente quante persone hanno downvoted questo post. Probabilmente dipendenti di MathWorks. –

+0

Purtroppo non esiste una libreria open source che offre ottime prestazioni. Ecco perché sto usando ArrayFire, perché almeno è gratuito per quello di cui ho bisogno. Praticamente tutte le funzioni di ArrayFire sono gratuite, ad eccezione di quelle che provengono da CULA, che è meglio di MAGMA per roba algebra lineare. Ma ArrayFire ha funzioni di algebra lineare a precisione singola libere, che uso abbastanza spesso. questo funzionerebbe per te? A proposito, il codice che hai postato non usa quelle caratteristiche di algebra lineare. –

1

Sono stati forniti da NVidia, quindi hanno buone prestazioni su schede compatibili CUDA. Le prestazioni reali dipendono dalla scheda stessa e dalla funzione che si sta utilizzando.

Nella mia esperienza solo cvRotate e cvResize hanno avuto prestazioni migliori rispetto a una normale CPU Intel. (Nota: mi interessavano solo le funzioni relative alle immagini)