Многопоточная обработка изображений в C ++ - PullRequest
11 голосов
/ 28 ноября 2008

Я работаю над программой, которая манипулирует изображениями разных размеров. Многие из этих манипуляций считывают пиксельные данные со входа и записывают в отдельный вывод (например, размытие). Это делается для каждого пикселя.

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

У меня есть несколько требований:

  • Размер исполняемого файла должен быть минимизирован. Другими словами, я не могу использовать массивные библиотеки. Какая самая легкая, переносимая библиотека потоков для C / C ++?
  • Размер исполняемого файла должен быть минимизирован. Я думал о наличии функции forEachRow (fp *), которая запускает поток для каждой строки, или даже forEachPixel (fp *), где fp работает с одним пикселем в своем собственном потоке. Какой лучше?
    • Должен ли я использовать нормальные функции или функторы или функционоиды или некоторые лямбда-функции или ... что-то еще?
    • В некоторых операциях используются оптимизации, для которых требуется информация из предыдущего обработанного пикселя. Это делает forEachRow выгодным. Будет ли лучше использовать forEachPixel даже с учетом этого?
  • Нужно ли блокировать массивы только для чтения и только для записи?
    • Входные данные считываются только из, но многие операции требуют ввода более одного пикселя в массиве.
    • Выход выводится только один раз на пиксель.
  • Скорость также важна (конечно), но оптимизация размера исполняемого файла имеет преимущество.

Спасибо.

Дополнительная информация по этой теме для любопытных: Библиотеки распараллеливания C ++: OpenMP против потоковых строительных блоков

Ответы [ 16 ]

0 голосов
/ 15 января 2010

Ваш компилятор не поддерживает OpenMP. Другой вариант - использовать библиотечный подход, доступны как Intel Threading Building Blocks, так и Microsoft Concurrency Runtime (VS 2010).

Существует также набор интерфейсов, называемых Parallel Pattern Library, которые поддерживаются обеими библиотеками, и в них есть шаблонный параллельный вызов библиотеки. так вместо:

#pragma omp parallel for 
for (i=0; i < numPixels; i++) 
{ ...} 

вы бы написали:

parallel_for(0,numPixels,1,ToGrayScale());

где ToGrayScale - функтор или указатель на функцию. (Обратите внимание, что если ваш компилятор поддерживает лямбда-выражения, которых он, скорее всего, не поддерживает, вы можете встроить функтор как лямбда-выражение).

parallel_for(0,numPixels,1,[&](int i)
{  
   pGrayScaleBitmap[i] = (unsigned BYTE)  
       (pRGBBitmap[i].red * 0.299 +  
        pRGBBitmap[i].green * 0.587 +  
        pRGBBitmap[i].blue * 0.114);  
});

-Rick

0 голосов
/ 15 января 2010

Вполне возможно, что узким местом является не процессор, а пропускная способность памяти, поэтому многопоточность не сильно поможет. Старайтесь минимизировать доступ к памяти и работать с ограниченными блоками памяти, чтобы можно было кэшировать больше данных. У меня была похожая проблема некоторое время назад, и я решил оптимизировать свой код для использования инструкций SSE. Увеличение скорости было почти в 4 раза на одну нить!

0 голосов
/ 29 ноября 2008

Я думаю, что независимо от выбранной вами модели потоков (boost, pthread, собственных потоков и т. Д.). Я думаю, что вы должны рассмотреть пул потоков, а не поток в строке. Потоки в пуле потоков очень дешевы для «запуска», так как они уже созданы в отношении ОС, просто нужно дать ей что-то сделать.

По сути, вы могли бы сказать, 4 темы в вашем пуле. Затем последовательно для каждого пикселя скажите следующему потоку в пуле потоков обработать пиксель. Таким образом, вы эффективно обрабатываете не более 4 пикселей за раз. Вы можете сделать размер пула либо по пользовательским предпочтениям, либо по количеству процессоров, которые сообщает система.

Это, безусловно, самый простой способ ИМХО добавить многопоточность в задачу SIMD.

0 голосов
/ 29 ноября 2008

Есть еще один вариант использования сборки для оптимизации. Теперь один захватывающий проект для динамической генерации кода - softwire (который датируется некоторое время - здесь - сайт оригинального проекта). Он был разработан Ником Капенсом и стал коммерчески доступным swiftshader . Но дополнительное оригинальное программное обеспечение по-прежнему доступно на gna.org.

Это может послужить введением в его решение.

Лично я не верю, что вы можете добиться значительной производительности, используя несколько потоков для вашей проблемы.

0 голосов
/ 28 ноября 2008

Может быть, написать свою собственную крошечную библиотеку, которая реализует несколько стандартных функций потоков, используя #ifdef для каждой платформы? В этом нет ничего особенного, и это уменьшит размер исполняемого файла намного больше, чем любая библиотека, которую вы могли бы использовать.

Обновление: А для распределения работы - разбейте свое изображение на части и дайте каждому потоку по кусочку. Так что, когда это сделано с частью, это сделано. Таким образом, вы избегаете реализации очередей заданий, которые еще больше увеличат размер вашего исполняемого файла.

0 голосов
/ 28 ноября 2008

Один поток на строку пикселей является безумным, лучше всего иметь от n-1 до 2n потоков (для n процессоров), и заставить каждый цикл извлекать одну рабочую единицу (может быть одна строка или другой тип раздела)

на Unix-подобных, используйте pthreads, это просто и легко.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...