Насколько хороша библиотека графического процессора OpenCV для матричных операций?

Я использую OpenCV для приложения в области компьютерного зрения. Я хотел бы ускорить некоторые матричные операции (матрицы довольно большие) на графическом процессоре и по возможности избежать кодирования непосредственно в CUDA C. OpenCV 2.4.1 имеет ряд функций с ускорением на GPU. Насколько хорошо они работают в вашем опыте? Может быть, лучше использовать другую библиотеку (например, Thrust)?

РЕДАКТИРОВАТЬ Пример приложения: Вычислить квадрат матрицы евклидовых расстояний на графическом процессоре. В настоящее время моя реализация с GPU-ускорением (и векторизация) в Matlab с использованием Parallel Computing Toolbox (PCT) примерно в 5-10 раз быстрее, чем моя реализация на C++ с OpenCV.

Реализация Матлаба:

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

ОБНОВЛЕНИЕ Вот еще одна реализация Matlab, которая выполняет то же самое (спасибо https://stackoverflow.com/a/7774323/1121420). Но он работает только на процессоре, потому что bsxfun не поддерживается PCT. Тем не менее, все еще ищет альтернативу С++.

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

person Alexey    schedule 29.06.2012    source источник
comment
Какие функции, в частности, вы планируете использовать?   -  person Paul R    schedule 29.06.2012
comment
Основные матричные вещи. gpu::reduce, gpu::multiply (умножение матрицы на элемент). А также умножение матриц, нахождение собственных значений и векторов матриц, транспонирование матриц.   -  person Alexey    schedule 29.06.2012
comment
@Alex - все простые матричные операции напрямую используют библиотеку NVidia (thrust?), Поэтому они очень хорошо оптимизированы.   -  person Martin Beckett    schedule 29.06.2012
comment
возможно, мой код на С++ недостаточно оптимален.   -  person Alexey    schedule 29.06.2012
comment
@Alex, можешь опубликовать свой код MATLAB/PCT? Если это так, я смогу опубликовать для вас полезный код.   -  person Ben Stewart    schedule 29.06.2012
comment
@BenStewart опубликовал код Matlab. Спасибо.   -  person Alexey    schedule 29.06.2012
comment
@BenStewart [nP, d] = size(P_gpu); [nQ, d] = size(Q_gpu); дает nP = 1581, nQ = 189 и d = 3   -  person Alexey    schedule 29.06.2012
comment
а значения P_gpu и Q_gpu находятся в диапазоне от 0 до 255.   -  person Alexey    schedule 29.06.2012
comment
спасибо, смотрите обновление ниже   -  person Ben Stewart    schedule 29.06.2012
comment
давайте продолжим это обсуждение в чате   -  person Ben Stewart    schedule 29.06.2012


Ответы (2)


Я считаю, что ArrayFire работает намного быстрее, и начал использовать его вместо ядер графического процессора в OpenCV для обработки изображений. Вот некоторые тесты Я обнаружил, что сравниваю ArrayFire (раньше он был в другом интерфейсе, называемом LibJacket) с OpenCV, и в моем бенчмаркинге также было верно, что ArrayFire в 2-4 раза быстрее, чем функции графического процессора в OpenCV. Из того, что я слышал, NVIDIA не писала ядра графического процессора в OpenCV, а передала их кому-то, возможно, поэтому они такие медленные. Поскольку я использую только 1 графический процессор, я могу использовать ArrayFire бесплатно.

Обновление, учитывая новый код MATLAB, опубликованный @Alex: я выполнил тест этого кода в своей системе. Я понимаю, что gpuArray в Parallel Computing Toolbox работает медленнее, чем CPU, но Jacket и ArrayFire пинают приклад. Технические характеристики аппаратного обеспечения:

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

Результаты сравнения процессора и графического процессора с использованием Parallel Computing Toolbox gpuArray (полностью прогретый). ЦП быстрее, чем 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.

Результаты сравнения процессора и графического процессора с использованием оболочки (полностью прогретой). Jacket превосходит PCT gpuArray в 3,7 раза и превосходит ЦП в 3 раза

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

Вот модифицированный код, который позволяет вам легко запускать все:

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

Куртка поддерживает BSXFUN на графическом процессоре и несколько повышает скорость:

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

Обратите внимание, что размеры, используемые здесь, довольно малы, поэтому большая часть кода CUDA, который пытается работать с этими небольшими размерами, скорее всего, будет работать плохо. Вот почему мне нравится использовать продукты AccelerEyes, потому что эти ребята чертовски оптимизировали GPU, в отличие от PCT gpuArray, Thrust, OpenCV, каждый из которых я пробовал в прошлом.

Вот результаты ArrayFire Free 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

Вот код ArrayFire, который я написал для этого:

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;
}
person Ben Stewart    schedule 29.06.2012
comment
прекрасная работа. Спасибо за предоставленные альтернативы. Сегодня определенно кое-что узнал: не знал о поддержке bsxfun в Jacket, и мне нравится простой код ArrayFire. Единственное, хотя и существует бесплатная версия библиотеки ArrayFire C++, бесплатная версия предлагает довольно ограниченную функциональность (например, она не поддерживает операции линейной алгебры). Я ищу библиотеку с открытым исходным кодом, можете ли вы предложить какую-нибудь? - person Alexey; 29.06.2012
comment
Пожалуйста. Удивительно, сколько людей проголосовало за этот пост. Вероятно, сотрудники MathWorks. - person Ben Stewart; 30.06.2012
comment
К сожалению, нет библиотеки с открытым исходным кодом, которая дает очень хорошую производительность. Вот почему я использую ArrayFire, потому что, по крайней мере, это бесплатно для того, что мне нужно. Практически все функции в ArrayFire бесплатны, за исключением тех, которые исходят от CULA, что лучше, чем MAGMA для линейной алгебры. Но в ArrayFire есть бесплатные функции линейной алгебры одинарной точности, которые я использую довольно часто. Это сработает для вас? Кстати, код, который вы разместили, не использует эти функции линейной алгебры. - person Ben Stewart; 30.06.2012
comment
Да, я не уверен насчет отрицательных голосов, хотелось бы, чтобы люди объяснили свои рассуждения. Я попробовал Matlab с Jacket для своего приложения, и он обеспечивает лучшую производительность (примерно в 3 раза) по сравнению с PCT. Посмотрим, смогу ли я увеличить производительность, используя бесплатную версию C++ ArrayFire. - person Alexey; 04.07.2012
comment
Я не совсем уверен, что происходит, но я попробовал прямой перенос вашего кода ArrayFire на R (используя RcppArrayfire), и он не выводит матрицу расстояний с 0 по диагонали даже на маленьких матрицах (поэтому я не думаю, что это ошибка математической аппроксимации). возможно, произошли изменения в работе некоторых функций? - person mkln; 10.02.2020
comment
^ нашел ошибку: две мозаичные матрицы нужно суммировать, а не умножать - person mkln; 10.02.2020

Они были предоставлены NVidia, поэтому имеют хорошую производительность на картах, совместимых с CUDA. Реальная производительность зависит от самой карты и используемой функции.

По моему опыту, только cvRotate и cvResize имели лучшую производительность, чем обычный процессор Intel. (Примечание: меня интересовали только функции, связанные с изображением)

person Mohammad    schedule 29.06.2012