Войти
ПрограммированиеФорумГрафика

CUDA/GPU как сделать максимально быстро свертку по ядру 3x3 , 4x4 , 5x5 ...

Страницы: 1 2 3 Следующая »
#0
13:34, 5 ноя. 2019

Процессор : Intel® Core™ i5-7500 CPU @ 3.40GHz × 4
Видеокарта : GeForce GTX 1050 Ti/PCIe/SSE2


Кодя ядра , исполняемый на CUDA/GPU 

+ Показать

-—————————————————————————————

Почему на свертку RGB-изображения расширениям 1024x1024 c ядром свертки 5x5 уходит примерно 4-5.миллисекунд .
Это нормально время , или можно это сделать гораздо быстрее . на данной конфигурации системы !????

PS: На один пиксель по RGB , выделяются 3 байта памяти !


#1
14:19, 5 ноя. 2019

werasaimon
> width, int height

попробуй через #define

#2
14:33, 5 ноя. 2019

Это учебное задание, нужно непременно через Куду?
А то свёртка макс. 5*5 - это слишком простая операция, будет упираться  в пересылку в видеопамять и обратно, так что процессор (при исп. хорошей библиотеки и многоядерности) вполне может быть быстрее.

#3
14:39, 5 ноя. 2019

Во-первых - избавься от вычисления всех констант, типа "i * kernelWidth + j", "offset*3", "kernelWidth / 2" и т.д. Все что можно вычислить - вычисляй заранее, до того как ты войдешь в цикл. Так же могу порекомендовать работать с векторами.

Во-вторых - kernel, я бы рекомендовал объявить его как константу, если есть такая возможность.

В-третьих - избавься от условий в цикле, просто пересчитай радиус свертки исходя из границ изображения, примерно так:

uvec2 left_top = abs( min((pos.xy-kernelHalfSize.xy), uvec2(0)) );
uvec2 right_bottom = kernelHalfSize.xy - abs( max (( (pos.xy+kernelHalfSize.xy) - imgSize.xy), uvec2(0)) );

for (int i = left_top.x; i < right_bottom.x; i++)
        {
            for (int j = left_top.y; j < right_bottom.y; j++)
            {
                uvec2 pixelPos.xy = pos.xy + (vec2(i,j)-kernelHalfSize.xy);

Код написан на коленке, так что нужно проверить, но идея такая - вытащить все что можно из тела цикла, максимально использовать константы, по возможности - работать с векторами.

Это то, что касается оптимизации самого кода, но можно еще оптимизировать алгоритм свертки, используя свойство сепарабельности, если применимо, это уменьшит количество операций с 5х5 до 5х2

#4
(Правка: 15:20) 15:14, 5 ноя. 2019

invis
> Это учебное задание, нужно непременно через Куду?
Да , Нужно именно через CUDA ..!


Fantom09
> Во-первых - избавься от вычисления всех констант, типа "i * kernelWidth + j",
> "offset*3", "kernelWidth / 2" и т.д. Все что можно вычислить - вычисляй
> заранее, до того как ты войдешь в цикл. Так же могу порекомендовать работать с
> векторами.
>
> Во-вторых - kernel, я бы рекомендовал объявить его как константу, если есть
> такая возможность.
>
> В-третьих - избавься от условий в цикле, просто пересчитай радиус свертки
> исходя из границ изображения, примерно так:
>
> uvec2 left_top = abs( min((pos.xy-kernelHalfSize.xy), uvec2(0)) );
> uvec2 right_bottom = kernelHalfSize.xy - abs( max (( (pos.xy+kernelHalfSize.xy)
> - imgSize.xy), uvec2(0)) );
>
> for (int i = left_top.x; i < right_bottom.x; i++)
>         {
>             for (int j = left_top.y; j < right_bottom.y; j++)
>             {
>                 uvec2 pixelPos.xy = pos.xy + (vec2(i,j)-kernelHalfSize.xy);
>
> Код написан на коленке, так что нужно проверить, но идея такая - вытащить все
> что можно из тела цикла, максимально использовать константы, по возможности -
> работать с векторами.
все это я уже делал , максимум что оно дало 1-2.FPS . ну это не очем  ....

Fantom09
> Это то, что касается оптимизации самого кода, но можно еще оптимизировать
> алгоритм свертки, используя свойство сепарабельности, если применимо, это
> уменьшит количество операций с 5х5 до 5х2
А в сепарабельном пространстве будет такой же результат , ну то есть если я поочередно пройдусь по осям , будет такой же результат , как и при обычной сверки . Ядро у меня может быть очень большим 20x20 и больше 

#5
15:30, 5 ноя. 2019

По сепарабельности, если это был вопрос - то да, результат одинаковый.
Добавлю ещё:
1) Видел рекомендацию кэшировать пиксели в локальной памяти. Вроде бы в OpenCV так сделано (у них OpenCL, но не суть). Кстати, можешь сравнить скорость OpenCV со своим.
2) Нвидия в примерах к своему SDK использует для Гаусса рекурсивный фильтр, что актуально для больших размеров фильтра (где-то 10+).

#6
17:04, 5 ноя. 2019

invis
У меня не в какую свертка OpenCV / CUDA не хочеть работать .

Может кто даст исходники с рабочим кодом OpenCV/CUDA .
Ну типа что типа этого
     

 Ptr<cv::cuda::Convolution> convolver = cv::cuda::createConvolution(cv::Size(3, 3));
 convolver->convolve(temp, kernel, result);

#7
17:40, 5 ноя. 2019

маленькие свёртки вроде 3х3 в лоб считаются хоть во фрагментном шейдере, хоть в аналоге на куде. интереснее считать свёртки с ядром большого радиуса (порядка размера изображения), это типично делается в фурье-пространстве через fft-преобразование.

#8
18:58, 5 ноя. 2019

werasaimon
> Может кто даст исходники с рабочим кодом OpenCV/CUDA .

Я толком не щупал последние версии OpenCV, только в исходники заглядывал.
Но по логике, если ты создаешь произвольный фильтр через createConvolution, то наверное должен сам его заполнить перед использованием.
Попробуй конкретный фильтр для начала:
https://docs.opencv.org/4.1.2/dc/d66/group__cudafilters.html
Смотри примеры, которые должны быть в комплекте с OpenCV.

#9
(Правка: 13:30) 13:30, 6 ноя. 2019

Suslik
> маленькие свёртки вроде 3х3 в лоб считаются хоть во фрагментном шейдере, хоть в
> аналоге на куде.
как быстрее будет в шейдере или на CUDA ....?

Suslik
> интереснее считать свёртки с ядром большого радиуса (порядка размера
> изображения), это типично делается в фурье-пространстве через
> fft-преобразование.
Можно прмер ....

#10
(Правка: 16:00) 15:58, 6 ноя. 2019

werasaimon
> как быстрее будет в шейдере или на CUDA ....?
никакой разницы

> Можно прмер ....
я пишу так:

auto res = image.clone().fft(true).multiply(kernel.clone().fft(true)).fft(false);
fft(true) — прямое inplace преобразование фурье, fft(false) — обратное, multiply — inplace попиксельное комплексное умножение. в фурье-базисе свёртка изображения image с ядром kernel равна попиксельному умножению одного на другое, поэтому переводишь в фурье, перемножаешь, переводишь обратно.

#11
16:52, 6 ноя. 2019

почитай это

#12
21:48, 6 ноя. 2019

Вот этот тред ещё почитай: https://twitter.com/SebAaltonen/status/1111207423757467648
У компута кеширование работает иначе, чем в PS, поэтому в лоб скопированный код может стать медленнее, чем в пиксельном

#13
2:54, 7 ноя. 2019

Я как-то сравнивал блюр на OpenGL и OpenCL, сейчас перепроверил - и действительно, если брать чистые вычисления, то GL (фрагментный шейдер) быстрее, причём в завимости от размера картинки, на самых больших до 1.5 раз. То есть доступ к памяти более эффективный, хотя в CL используются те же самые текстуры (images).
Может быть, в OCL можно добиться лучшей скорости за счёт тюнинга размера и формы local work group, но я этим не занимался, потому что блюр с маленьким ядром упирался в основном в трансфер, а по трансферам OCL и так выигрывал с разгромным счётом.

#14
6:48, 7 ноя. 2019

invis
> если брать чистые вычисления, то GL (фрагментный шейдер) быстрее
На нвидии хорошо работает хардварное сжатие текстур, что сильно увеличивает пропускную способность памяти, но это только для рисования, для компьют не работает, отсюда и выигрышь.
На АМД компьют может быть побыстрее, так как сжатие работает не очень эффективно.

Страницы: 1 2 3 Следующая »
ПрограммированиеФорумГрафика