Обработка байтовых пикселей с помощью SSE / SSE2 в C - PullRequest
5 голосов
/ 22 декабря 2009

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

Однако у меня проблема с форматом данных. Моя библиотека веб-камеры дает мне кадры веб-камеры в виде указателя (void *) на буфер, содержащий 24- или 32-битные байтовые пиксели в формате ABGR или BGR. Я приводил их к типу char *, чтобы ptr ++ и т.д. вели себя правильно. Однако все операции SSE / SSE2 ожидают либо четыре целых числа, либо четыре числа с плавающей запятой в типах данных __m128 или __m64. Если я сделаю это (при условии, что я прочитал значения цвета из буфера в символы r, g и b):

float pixel [] = {(float) r, (float) g, {float) b, 0.0f};

затем загрузить другой массив с плавающей точкой, полный констант

константы с плавающей точкой [] = {0,299, 0,587, 0,114, 0,0f};

приведите оба указателя с плавающей точкой к __m128 и используйте встроенную функцию __mm_mul_ps для выполнения r * 0.299, g * 0.587 и т. Д. и т. д. нет общего прироста производительности, потому что все перемешивание занимает столько времени!

Есть ли у кого-нибудь предложения о том, как быстро и эффективно загрузить эти значения байтовых пикселей в регистры SSE, чтобы я действительно получил выигрыш в производительности, работая с ними как таковой?

Ответы [ 3 ]

1 голос
/ 22 декабря 2009

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

Если я хорошо помню, это приведение составляет около 50 тактов в большинстве архитектур ... и учитывая наихудший случай, когда умножение FP может занять, скажем, около 4 тактов каждый без наложения в конвейере, делая все они параллельно в 1 цикле могут сэкономить не более 15 циклов, но все равно без усиления.

Я бы определенно пошел работать всегда с одним и тем же числовым форматом (в данном случае целым числом), если бы он передавался с MMX, как сказал Shmoopty, то лучше.

1 голос
/ 22 декабря 2009

Если вы хотите использовать MMX ...

MMX предоставляет вам набор из 64-битных регистров, которые могут обрабатывать каждый регистр как 8-битные значения

Как и 8-битные значения, с которыми вы работаете.

Здесь есть хороший праймер .

0 голосов
/ 22 декабря 2009

Во-первых, данные, из которых вы копируете (думаю, на них указывает указатель void*), должны быть выровнены по памяти для оптимальной производительности - если не скопировать их в буфер, выровненный по памяти.

Во-вторых, вы все еще можете использовать SSE2 после перемещения данных в буфер, выровненный по памяти, это довольно просто - я использовал код здесь без каких-либо проблем со встроенными функциями (но были проблемы с сборка как подробно здесь ).

Надеюсь, это полезно - я тоже работал с изображениями и сохранил их как unsigned char в основной памяти и скопировал их в регистры SSE2 (имело смысл, поскольку R, G или B варьировались от 0 до 255) - но я использовал ассемблерный код, потому что я чувствовал, что это проще.

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

Удачи!

...