Почему удаление инструкций из моей встроенной функции SSE замедляет ее работу?C++

Программы на C++. Форум разработчиков
Ответить Пред. темаСлед. тема
Anonymous
 Почему удаление инструкций из моей встроенной функции SSE замедляет ее работу?

Сообщение Anonymous »

Обратите внимание, что этот вопрос не касается преобразования YUV422 в RGB!
У меня есть этот код для преобразования порядка пикселей YUV422 в RGB. >

Код: Выделить всё

static void yuv422ToRGB(unsigned char* img,
int width, int height, int widthStep, unsigned char* dst)
{

__m128i yMask1 = _mm_setr_epi8(0, -1, 0, -1, 0, -1, 4, -1, 4, -1, 4, -1, 8, -1, 8, -1);
__m128i yMask2 = _mm_setr_epi8(8, -1, 12, -1, 12, -1, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1);
__m128i uvMask1 = _mm_setr_epi8(1, 3, 1, 3, 1, 3, 5, 7, 5, 7, 5, 7, 9, 11, 9, 11);
__m128i uvMask2 = _mm_setr_epi8(9, 11, 13, 15, 13, 15, 13, 15, -1, -1, -1, -1, -1, -1, -1, -1);
__m128i magicMask1 = _mm_setr_epi8(0, 102, 25, 52, -127, 0, 0, 102, 25, 52, -127, 0, 0, 102, 25, 52);
__m128i magicMask2 = _mm_setr_epi8(-127, 0, 0, 102, 25, 52, -127, 0, 0, 0, 0, 0, 0, 0, 0, 0);
__m128i const128 = _mm_set1_epi8(-127);
__m128i const16 = _mm_set1_epi16(16);
__m128i const32 = _mm_set1_epi16(32);
__m128i const74 = _mm_set1_epi16(74);
__m128i gFlipMask1 = _mm_setr_epi16(1, -1, 1, 1, -1, 1, 1, -1);
__m128i gFlipMask2 = _mm_setr_epi16(1, 1, -1, 1, 0, 0, 0, 0);

for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x += 16)
{
__m128i imgreg1 = _mm_loadu_si128((__m128i*)(img + x));
__m128i yValues1 = _mm_shuffle_epi8(imgreg1, yMask1);
yValues1 = _mm_subs_epi16(yValues1, const16);
yValues1 = _mm_mullo_epi16(yValues1, const74);

__m128i uvValues1 = _mm_shuffle_epi8(imgreg1, uvMask1);
uvValues1 = _mm_xor_si128(uvValues1, const128);

__m128i toAdd1 = _mm_maddubs_epi16(magicMask1, uvValues1);
toAdd1 = _mm_mullo_epi16(toAdd1, gFlipMask1);
toAdd1 = _mm_add_epi16(toAdd1, const32);

__m128i rgb1 = _mm_add_epi16(yValues1, toAdd1);
rgb1 = _mm_srai_epi16(rgb1, 6);

//======================

__m128i yValues2 = _mm_shuffle_epi8(imgreg1, yMask2);
yValues2 = _mm_subs_epi16(yValues2, const16);
yValues2 = _mm_mullo_epi16(yValues2, const74);

__m128i uvValues2 = _mm_shuffle_epi8(imgreg1, uvMask2);
uvValues2 = _mm_xor_si128(uvValues2, const128);

__m128i toAdd2 = _mm_maddubs_epi16(magicMask2, uvValues2);
toAdd2 = _mm_mullo_epi16(toAdd2, gFlipMask2);
toAdd2 = _mm_add_epi16(toAdd2, const32);

__m128i rgb2 = _mm_add_epi16(yValues2, toAdd2);
rgb2 = _mm_srai_epi16(rgb2, 6);

//======================

__m128i out = _mm_packus_epi16(rgb1, rgb2);
_mm_storeu_si128((__m128i*)(dst), out);
dst += 12;
}
img += widthStep;
}
}
У меня возникла идея сделать это быстрее, используя альтернативный метод расчета, который потребует меньшего количества инструкций. Чтобы проверить, какую производительность я могу получить, я раскомментировал шесть инструкций внутри цикла, которые можно было сохранить. Остальная часть кода осталась без изменений:

Код: Выделить всё

static void yuv422ToRGB(unsigned char* img,
int width, int height, int widthStep, unsigned char* dst)
{

__m128i yMask1 = _mm_setr_epi8(5, -1, 0, -1, 0, -1, 4, -1, 4, -1, 4, -1, 8, -1, 8, -1);
__m128i yMask2 = _mm_setr_epi8(8, -1, 12, -1, 12, -1, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1);
__m128i uvMask1 = _mm_setr_epi8(1, 3, 1, 3, 1, 3, 5, 7, 5, 7, 5, 7, 9, 11, 9, 11);
__m128i uvMask2 = _mm_setr_epi8(9, 11, 13, 15, 13, 15, 13, 15, -1, -1, -1, -1, -1, -1, -1, -1);
__m128i magicMask1 = _mm_setr_epi8(0, 102, 25, 52, -127, 0, 0, 102, 25, 52, -127, 0, 0, 102, 25, 52);
__m128i magicMask2 = _mm_setr_epi8(-127, 0, 0, 102, 25, 52, -127, 0, 0, 0, 0, 0, 0, 0, 0, 0);
__m128i const128 = _mm_set1_epi8(-127);
__m128i const16 = _mm_set1_epi16(16);
__m128i const32 = _mm_set1_epi16(32);
__m128i const74 = _mm_set1_epi16(74);
__m128i gFlipMask1 = _mm_setr_epi16(1, -1, 1, 1, -1, 1, 1, -1);
__m128i gFlipMask2 = _mm_setr_epi16(1, 1, -1, 1, 0, 0, 0, 0);

for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x += 16)
{
__m128i imgreg1 = _mm_loadu_si128((__m128i*)(img + x));
__m128i yValues1 = _mm_shuffle_epi8(imgreg1, yMask1);
//yValues1 = _mm_subs_epi16(yValues1, const16);
//yValues1 = _mm_mullo_epi16(yValues1, const74);

__m128i uvValues1 = _mm_shuffle_epi8(imgreg1, uvMask1);
uvValues1 = _mm_xor_si128(uvValues1, const128);

__m128i toAdd1 = _mm_maddubs_epi16(magicMask1, uvValues1);
toAdd1 = _mm_mullo_epi16(toAdd1, gFlipMask1);
//toAdd1 = _mm_add_epi16(toAdd1, const32);

__m128i rgb1 = _mm_add_epi16(yValues1, toAdd1);
rgb1 = _mm_srai_epi16(rgb1, 6);

//======================

__m128i yValues2 = _mm_shuffle_epi8(imgreg1, yMask2);
//yValues2 = _mm_subs_epi16(yValues2, const16);
//yValues2 = _mm_mullo_epi16(yValues2, const74);

__m128i uvValues2 = _mm_shuffle_epi8(imgreg1, uvMask2);
uvValues2 = _mm_xor_si128(uvValues2, const128);

__m128i toAdd2 = _mm_maddubs_epi16(magicMask2, uvValues2);
toAdd2 = _mm_mullo_epi16(toAdd2, gFlipMask2);
//toAdd2 = _mm_add_epi16(toAdd2, const32);

__m128i rgb2 = _mm_add_epi16(yValues2, toAdd2);
rgb2 = _mm_srai_epi16(rgb2, 6);

//======================

__m128i out = _mm_packus_epi16(rgb1, rgb2);
_mm_storeu_si128((__m128i*)(dst), out);
dst += 12;
}
img += widthStep;
}
}
Профилируя модифицированную версию, оказывается, что она примерно на 10% медленнее исходной (~2,2 мс против 1,9 мс). Для меня это не имеет никакого смысла. Я ожидал, что это не будет иметь никакого значения, поскольку узкое место находится где-то в другом месте, но как меньшее количество инструкций может замедлить работу кода?
Что я проверял:
  • Вывод компилятора https://godbolt.org/z/893c8EfPW
  • Эффекты кэширования. Я протестировал следующий порядок вызовов: A A B B A B. Все вызовы A были быстрее, чем вызовы B.
Платформа:
  • Intel Atom E3845
  • clang 14
  • Флаги компилятора: -std=c++14 -O3 - March=silvermont
Я попытался сделать пример меньшего размера, который показывает то же поведение, но как только я изменю некоторые части кода , например вычислите только yValues1, аномалия исчезнет.
Как можно объяснить такое поведение в общих чертах?

Подробнее здесь: https://stackoverflow.com/questions/786 ... -it-slower
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Почему необходимо выравнивание SSE при выполнении инструкций SIMD?
    Anonymous » » в форуме C++
    0 Ответы
    16 Просмотры
    Последнее сообщение Anonymous
  • SSE с sse-starlette и FastAPI не отвечает [дубликат]
    Anonymous » » в форуме Python
    0 Ответы
    24 Просмотры
    Последнее сообщение Anonymous
  • Серверные события (SSE) с использованием python httpx-sse
    Anonymous » » в форуме Python
    0 Ответы
    21 Просмотры
    Последнее сообщение Anonymous
  • SSE (событие на стороне сервера) прошло CSE (Client Server Event) - LOOP SSE.PHP работает только с Curl
    Anonymous » » в форуме Php
    0 Ответы
    5 Просмотры
    Последнее сообщение Anonymous
  • SSE (событие на стороне сервера) прошло CSE (Client Server Event) - LOOP SSE.PHP работает только с Curl
    Anonymous » » в форуме Javascript
    0 Ответы
    5 Просмотры
    Последнее сообщение Anonymous

Вернуться в «C++»