CUDA small kernel 2d свертка - как это сделать

Я экспериментировал с ядрами CUDA в течение нескольких дней, чтобы выполнить быструю двумерную свертку между изображением 500x500 (но я также мог изменять размеры) и очень маленькое 2D-ядро (ядро лапласиана 2d, поэтому это ядро ​​3x3. слишком мал, чтобы получить огромное преимущество во всех потоках cuda).

Я создал классическую реализацию CPU (два для циклов, так же легко, как вы думаете), а затем я начал создавать ядра CUDA.

После нескольких неутешительных попыток выполнить более быструю свертку я закончил с этим кодом: http://www.evl.uic.edu/sjames/cs525/final.html (см. раздел "Общая память" ), он в основном позволяет блоку потоков 16x16 загружать все данные свертки, которые ему нужны в общей памяти, а затем выполняет свертку.

Ничего, процессор все еще намного быстрее. Я не пытался использовать метод FFT, потому что CUDA SDK утверждает, что он эффективен при больших размерах ядра.

Независимо от того, читаете ли вы все, что я написал, мой вопрос:

как я могу выполнить быструю двумерную свертку между относительно большим изображением и очень маленьким ядром (3x3) с CUDA?

Ответ 1

Вы правы в том, что ядро ​​3x3 не подходит для подхода на основе FFT. Лучшим способом справиться с этим было бы толкать ядро ​​в постоянную память (или если вы используете карточку fermi +, это не должно иметь большого значения).

Поскольку вы знаете размер ядра, самым быстрым способом сделать это будет чтение фрагментов входного изображения/сигнала в разделяемую память и выполнение развернутой операции умножения и добавления.

-

Если вы хотите использовать библиотеки для выполнения этой операции ArrayFire и OpenCV имеют высоко оптимизированные подпрограммы Convolution, которые могут сэкономить вам много времени на разработку.

Я не очень хорошо знаком с OpenCV, но в ArrayFire вы можете сделать что-то вроде следующего.

array kernel = array(3, 3, h_kernel, afHost); // Transfer the kernel to gpu
array image  = array(w, h, h_image , afHost); // Transfer the image  to gpu
array result = convolve2(image, kernel);       // Performs 2D convolution

ИЗМЕНИТЬ

Дополнительным преимуществом использования ArrayFire является его пакетная операция, позволяющая параллельно выполнять свертку. Вы можете прочитать о том, как convolvutions поддерживают пакетные операции над здесь

Например, если у вас было 10 изображений, которые вы хотите свернуть, используя одно и то же ядро, вы можете сделать что-то вроде следующего:

array kernel = array(3, 3, h_kernel, afHost);     // Transfer the kernel to gpu
array images = array(w, h, 10, h_images, afHost); // Transfer the images to gpu
array res    = convolve2(images, kernel); // Perform all operations simultaneously

-

Полное раскрытие информации: я работаю в AccelerEyes и активно работаю над ArrayFire.