2012-06-29 7 views
8

Estoy usando OpenCV para una aplicación en visión artificial. Me gustaría acelerar algunas operaciones matriciales (las matrices son bastante grandes) en GPU y quiero evitar la codificación directamente en CUDA C, si es posible. OpenCV 2.4.1 tiene una serie de funciones aceleradas GPU. ¿Qué tan bien se desempeñan en tu experiencia? ¿Me conviene más usar otra biblioteca (por ejemplo, Thrust)?¿Qué tan bueno es la biblioteca OpenCV GPU para operaciones matriciales?

EDIT Aplicación de ejemplo: Calculate squared Euclidean distance matrix on GPU. Actualmente, mi implementación acelerada (y vectorizada) de GPU en Matlab utilizando Parallel Computing Toolbox (PCT) es aproximadamente 5-10 veces más rápida que la implementación de C++ con OpenCV.

aplicación 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 

ACTUALIZACIÓN Aquí hay otra aplicación Matlab que realiza lo mismo (gracias a https://stackoverflow.com/a/7774323/1121420). Pero solo se ejecuta en la CPU porque PCT no es compatible con bsxfun. Aún estoy buscando una alternativa C++.

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

¿Qué funciones, en particular estás considerando utilizar? –

+0

Cosas básicas de matriz. gpu :: reduce, gpu :: multiplicar (por multiplicación de matrices de elementos). Además, la multiplicación de matrices, la búsqueda de autovalores y vectores propios de la matriz, la transposición de la matriz. – Alexey

+1

@Alex: todas las operaciones simples de matriz utilizan directamente la biblioteca NVidia (¿de empuje?), Así que están muy bien optimizadas –

Respuesta

3

Encuentro ArrayFire mucho más rápido y comencé a usarlo en lugar de los núcleos GPU en OpenCV para el procesamiento de imágenes. Aquí están some benchmarks Encontré comparando ArrayFire (solía estar en una interfaz diferente llamada LibJacket) para OpenCV y también ha sido cierto en mi evaluación comparativa que ArrayFire es 2-4 veces más rápido que las funciones de la GPU en OpenCV. Por lo que he escuchado, NVIDIA no escribió los núcleos de la GPU en OpenCV, sino que los contrató a alguien, y es por eso que son muy lentos. Como solo uso 1 GPU, puedo usar ArrayFire gratis.

Actualización, dado el nuevo código de MATLAB publicado por @Alex: Corrí el punto de referencia de este código en mi sistema. Entiendo que el gpuArray de Parallel Computing Toolbox es más lento que el CPU, pero Jacket y ArrayFire patean el trasero. especificaciones HW son:

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

Resultados de CPU vs GPU usando Parallel Computing Toolbox gpuArray (completamente calentado). CPU es más rápido que 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. 

Resultados de CPU vs GPU utilizando Jacket (completamente calentado). Jacket beats PCT gpuArray por 3.7X y golpea a la CPU por 3X

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

Aquí está el código modificado que vamos a ejecutar los que fácilmente:

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 

chaqueta hace de soporte BSXFUN en la GPU, y lo hace mejorar las velocidades de algo :

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

Tenga en cuenta que los tamaños utilizados aquí son bastante pequeñas, por lo que la mayoría del código CUDA que intenta ejecutar en estos tamaños pequeños es probable que un mal desempeño. Es por eso que me gusta usar las cosas de AccelerEyes, porque esos tipos han optimizado la GPU, a diferencia de PCT gpuArray, Thrust, OpenCV, cada uno de los cuales he probado en el pasado.

Aquí es el resultado ArrayFire libre de C++:

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 

Aquí está el código ArrayFire que escribí para esto:

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

buen trabajo. Gracias por proporcionar las alternativas. Definitivamente aprendí algo hoy: no sabía sobre el soporte de Jacket para bsxfun y me gusta el código simple de ArrayFire. Lo único es que, aunque hay una versión gratuita de la biblioteca ArrayFire C++, la versión gratuita ofrece una funcionalidad bastante limitada (por ejemplo, no admite operaciones de álgebra lineal). Estoy buscando una biblioteca de código abierto, ¿puedes sugerir alguna? – Alexey

+0

De nada. Sorprendente la cantidad de gente que votó negativamente por esta publicación. Probablemente los empleados de MathWorks. –

+0

Desafortunadamente, no es una biblioteca de código abierto que ofrece muy buen rendimiento. Es por eso que he estado usando ArrayFire, porque al menos es gratis para lo que necesito. Prácticamente todas las funciones de ArrayFire son gratuitas, excepto las que provienen de CULA, que es mejor que MAGMA para el álgebra lineal. Pero ArrayFire tiene funciones gratuitas de álgebra lineal de precisión simple, que uso con bastante frecuencia. ¿Eso funcionaría para ti? Por cierto, el código que publicaste no usa esas características de álgebra lineal. –

1

Han sido contribuidos por NVidia, por lo que tiene un buen rendimiento en tarjetas compatibles con CUDA. El rendimiento real depende de la tarjeta en sí y de la función que está utilizando.

En mi experiencia, solo cvRotate y cvResize tuvieron un mejor rendimiento que una CPU Intel normal. (Nota: solo me interesaron las funciones relacionadas con la imagen)

Cuestiones relacionadas