-
Привет! Требуется ускорить процесс обработки графических данных. Возможно ли обрабатывать массив из потоков и ускорит ли выделение в поток функций обработки? Вроде видел тут Sapersky выкладывал демку с отрисовкой в потоках. Там скорость за 1000 fps зашкаливала.
-
Dmk, ты ли это? Привет. Да ускоряется. Скорость зависит от числа вычислительных блоков. То бишь ядер.
1000 fps что-то не верится. Хотя тут вроде была заливка белым цветом. У меня функции обработки работают со следующей скоростью. Все тесты проводились на 512х512. 0.7 мс Инвертирования цвета. 10-15 мс фильтрация типа Blur. 30-50 мс HSV, Кэнни, Угловой детектор.
Распределение задач можно сделать по кадрам. Соответственно за х мс будет 4 или 8 кадров (4 ядра 8 потоков).
Так что FPS порядка 80-400 вполне реально.
Могу даже пример с батцать.
-
Ускорит, если обработка не слишком простая, иначе может упереться в память. И если не будет зависимостей потоков друг от друга. В старом примере картинка разбивалась по вертикали на несколько полосок, каждый поток работал со своей полоской. Можно делать так, но есть более изящный метод, который позволяет прикручивать многопоточность почти к чему угодно с минимальными правками - parallel for. Заменяем внешний цикл обработки картинки for y:=0 to Dst.AbsHeight-1 do на что-то вроде Parallel.For(0, Dst.AbsHeight-1).Execute(
procedure(y: integer) и всё, ну ещё некоторые переменные перенести в анонимную функцию, и убедиться, что итерации независимы друг от друга. Правда, нормальной библиотеки для этого дела я не нашёл. OTL почему-то даёт большие накладные расходы (на создание пула потоков?). Встроенный parallel for в XE7+ мне не понравился тем, что нельзя отключить многопоточность для отладки. Собственный велосипед толком не дописан, хотя могу выложить, если интересно. Опять же, Intel в своей IPP отказывается от многопоточности на низком уровне - утверждают, что неэффективно. Может, они и правы. Я толком не применял parallel for в рабочих проектах из-за пристрастия к старым версиям Дельфи, без анонимных функций не так красиво получается.
-
>Dmk, ты ли это? Не работал я с потоками. Вот планирую заняться :(
>1000 fps что-то не верится. Было-было. У меня GTX 980 и 6 ядер i7.
На самом деле у меня масштабирование с использованием MMX. Почти все на ассемблере. Хотел сделать, чтобы другие ядра работали тоже. На больших файлах проседает производительность. Мне бы небольшой намек как в потоке обращаться к массиву, а там я сам доделаю. Думаю флаги какие то ставить надо, чтобы по 2-3 раза один и тот же участок памяти не читался.
-
Какая у вас обработка?
-
Есть такой цикл: for lpY := 0 to (Dest.qHeight - 1) do
begin
FProcScaleScLines(lpSrc, lpDest, lpWidth, Src.qExtra.eScLen);
lpSrc := lpSrc + (Src.qExtra.eScLen shl 1);
lpDest := lpDest + Dest.qExtra.eScLen;
end; FProcScaleScLines - это перегружаемая процедура, которая масштабирует скан-линии. Вот этот цикл я и хотел в поток загнать. Только как сделать, чтобы в потоке разные скан-линии выбирались из памяти? procedure TImageStream.ScaleChannelScLines64A(Src, Dest, NumPixels, ScLen: uint64);
asm
mov r11, rsi
mov r10, rdi
push rbx
mov rsi, src
mov rdi, dest
mov rdx, Self.FNumChannels
mov rcx, NumPixels
shr rcx, 1
mov r8, ScLen
mov r9, r8
add r9, rdx
xor ax, ax
@@NextBytes:
xor bx, bx
mov al, [rsi]
add bx, ax
mov al, [rsi+rdx]
add bx, ax
mov al, [rsi+r8]
add bx, ax
mov al, [rsi+r9]
add bx, ax
shr bx, 2
mov [rdi], bl
add rdi, rdx
add rsi, rdx
add rsi, rdx
dec rcx
jnz @@NextBytes
pop rbx
mov rdi, r10
mov rsi, r11
end;
-
Также есть процедуры Альфаблендинга и т.п. Хотел попробовать тоже в поток поместить. На одном потоке как то медленно получается.
-
Создаешь N+1 поток. Босс + N рабочих.
Даёшь задачу босу подсчитать сумму чисел в массиве. Босс делит весь массив на участки по числу рабочих. И отправляет сообщения рабочим. В сообщение указывается массив откуда начинать суммировать где заканчивать.
К примеру первому рабочему картинка BMP, строки с 0 по 99 второму рабочему картинка BMP, строки с 100 по 199 ... восьмому и последнему рабочему картинка BMP, строки с 700 по 799
Рабочий получает участок и начинает обрабатывать.
Далее Босс ждёт пока все рабочие закончат работать. Как только все закончили бос радостно докладывает тому кто его вызвал что задача закончена.
for i:=1 to Fabrick.Wokers.count-1 do begin cmd:=TSumData.Create; TSumData(cmd).Data:=TSumData(TasksList[0]).Data; TSumData(cmd).Count:=TSumData(TasksList[0]).Count;
cmd.Count:=cmd.Count div Fabrick.Woker.Count; {cmd.Count -размер задачи} Fabrick.Wokers[i].SendCommand(cmd); {отправляем каждому рабочему свою команду} end;
for i:=1 to Fabrick.Wokers.Count-1 do Fabrick.Wokers[i].Resume; {Пинаем рабочего, что-бы тот проснулся и начал делать работу}
for i:=1 to Fabrick.Wokers.Count-1 do Fabrick.Wokers[i].WaitFor; {ждем пока рабочие закончат работать}
Fabrick - тут у меня это пул потоков. Только мне бассейн не нужен. Я рабочих в чёрном теле держу.
procedure TWoker.Execute; begin if Task<>nil then Task.Exec; {Если есть задача выполняем} ReturnValue:=1; {Нужно для WaitFor} end;
procedure TSumData.Exec; var i:Integer; Massiv:PByte; Sum:Integer; begin // while not Terminated do {Добавить по желанию, чтобы ещё раз не делить задачу и не загромождать код не стал писать} Massiv:=Data; Sum:=0; for i := 0 to Count-1 do {Для простоты, тут Count это тот который размер задачи, см код Босса } begin Sum:=Sum+Massiv^; Inc(Massiv); end; Self.Goal:=Self.Goal+Sum; {Goal это у меня Result такой фирменный} inherited Exec; { Вызываем предка что-бы он установил число обработанных байт Compiletion:=Count;} end;
-
> Dmk © (09.03.16 21:38) [5]
У вас смотрю Delphi новая там проще. Берём ваш цикл TParallel.For(0, Dest.qHeight - 1, procedure (lpY: Integer) begin //Масштабируем 2 линии в одну FProcScaleScLines(lpSrc, lpDest, lpWidth, Src.qExtra.eScLen); //Следующие 2 скан-линии lpSrc := lpSrc + (Src.qExtra.eScLen shl 1); //Адрес следующей линии lpDest := lpDest + Dest.qExtra.eScLen; end);
Тут дефи сам из procedure (lpY: Integer) begin ... создаёт объект и метод внутри него.
-
-
>У вас смотрю Delphi новая там проще. Не, это в XE10. У меня XE6. В ней нет поддержки TParallel.
-
-
Да вчера видимо уже спал. Конечно не инкриментом. А непосредственное от lpY.
lpSrc := lpY* (Src.qExtra.eScLen shl 1); //Адрес следующей линии lpDest := lpY* Dest.qExtra.eScLen;
-
> Dmk © (09.03.16 18:18)
возможно имеет смысл всеми способами постараться переложить работу на GPU. уверен, что прирост производительности будет просто огромный, если грамотно реализовать.
-
Eraser © (11.03.16 03:49) [13] Имеется ввиду OpenGL, DirectX и т.п.?
-
Eraser © (11.03.16 03:49) [13]Все бы замечательно, если бы OpenGL или DirectX считали матрицы. Это бы мне помогло. что-то типа этого: movq xmm4, [_x]
movq xmm5, [_y]
movq xmm6, [_z]
movq xmm7, _mulByte
pxor xmm0, xmm0
mov rdi, XYZRGB_addr
movq xmm1, [rdi]
movq xmm2, [rdi + 8]
movq xmm3, [rdi + 16]
mulpd xmm1, xmm4
mulpd xmm2, xmm5
mulpd xmm3, xmm6
addpd xmm0, xmm1
addpd xmm0, xmm2
addpd xmm0, xmm3
mulpd xmm0, xmm7
movq [_x], xmm0
-
Или экспоненту с логарифмом считали бы быстрее. Проц тормозит :(
-
Всё они могут посчитать. Другой вопрос, что при однократной обработке картинки пересылка в видеокарту и обратно может всё затормозить. Распиши подробнее, что делаешь - а то как-то всё обрывками, начали с масштабирования и блендинга, закончили логарифмами.
-
> Dmk © (11.03.16 11:21) [14]
я редко на практике сталкивался с DX и OpenGL, однако, все таки сталкивался. На сколько я успел заметить, работа с матрицами, это один из краеугольных моментов данных технологий. Но я имел ввиду другое, возможно, надо переложить всю работу по требуемой обработке изображения на эти технологии. Т.е. написать какой-то шейдер и пусть оно само там производит нужные преобразования. Ну или что-то вроде этого https://msdn.microsoft.com/ru-ru/library/windows/desktop/ff684172(v=vs.85).aspx
-
> Или экспоненту с логарифмом считали бы быстрее. Проц тормозит > :(
Логарифм в ядре считает 1 блок. Если вы распараллеливании при помощи SIMD может несколько. А вот в видео карте таких блоков много. И для экспоненты тоже. Вот только это ещё от видео карты зависит.
> Все бы замечательно, если бы OpenGL или DirectX считали > матрицы.
Они и считают. Еще моя Geforce4MX так умела.
Но нет смысла использовать OpenGL и DirectX. Если видео карта NVidia то надо использовать CUDA. Если видео карта ATI то надо использовать ATI Firmvare - сейчас это OpenCL Вот под Intel надо смотреть что быстрее шредер или OpenCL
|