Итак, я не знаю, что делает этот код, однако я знаю, что вы спрашиваете, как оптимизировать операторов ternery и получить эту часть кода, работающую только в SSE. В качестве первого шага я бы рекомендовал попробовать подход с использованием целых флагов и умножения, чтобы избежать условного оператора. Например:
В этом разделе
for(int m=0; m < PBS_SSE_PIXELS_PROCESS_AT_ONCE; m++)
{
bool bIsEvenFloor = vn1.m128i_u16[m]==0;
vnPxChroma.m128i_u16[m] = m%2==0 ?
(bIsEvenFloor ? vnPxCeilChroma.m128i_u16[m] : vnPxFloorChroma.m128i_u16[m]) :
(bIsEvenFloor ? vnPxFloorChroma.m128i_u16[m] : vnPxCeilChroma.m128i_u16[m]);
}
синтаксический эквивалент этой
// DISCLAIMER: Untested both in compilation and execution
// Process all m%2=0 in steps of 2
for(int m=0; m < PBS_SSE_PIXELS_PROCESS_AT_ONCE; m+=2)
{
// This line could surely pack muliple u16s into one SSE2 register
uint16 iIsOddFloor = vn1.m128i_u16[m] & 0x1 // If u16[m] == 0, result is 0
uint16 iIsEvenFloor = iIsOddFloor^0x1 // Flip 1 to 0, 0 to 1
// This line could surely perform an SSE2 multiply across multiple registers
vnPxChroma.m128i_u16[m] = iIsEvenFloor * vnPxCeilChroma.m128i_u16[m] +
iIsOddFloor * vnPxFloorChroma.m128i_u16[m]
}
// Process all m%2!=0 in steps of 2
for(int m=1; m < PBS_SSE_PIXELS_PROCESS_AT_ONCE; m+=2)
{
uint16 iIsOddFloor = vn1.m128i_u16[m] & 0x1 // If u16[m] == 0, result is 0
uint16 iIsEvenFloor = iIsOddFloor^0x1 // Flip 1 to 0, 0 to 1
vnPxChroma.m128i_u16[m] = iIsEvenFloor * vnPxFloorChroma.m128i_u16[m] +
iIsOddFloor * vnPxCeilChroma.m128i_u16[m]
}
основного расщепления на две петлю вы теряете повышение производительности последовательного доступа к памяти, но уронить операцию по модулю и два условные операторы.
Теперь вы говорите, что есть два булевых оператора для каждого цикла, а также множители , которые я мог бы добавить, не являются встроенными реализациями SSE. Что хранится в вашем массиве vn1.m123i_u16 []? Это только нули и единицы? Если вам не нужна эта часть и она может покончить с этим. Если нет, можете ли вы нормализовать свои данные в этом массиве только для нулей и единиц? Если массив vn1.m123i_u16 содержит только единицы и нули, то этот код становится
uint16 iIsOddFloor = vn1.m128i_u16[m]
uint16 iIsEvenFloor = iIsOddFloor^0x1 // Flip 1 to 0, 0 to 1
Вы также заметите, что я не использую SSE умножает для выполнения isEvenFloor * vnPx... part
, ни хранить iIsEvenFloor
и iIsOddFloor
регистров. Мне жаль, что я не помню, что SSE-intrinsics для u16 размножается/регистрируется сверху, но, тем не менее, я надеюсь, что этот подход будет полезен. Некоторые оптимизации вы должны смотреть, чтобы:
// This line could surely pack muliple u16s into one SSE2 register
uint16 iIsOddFloor = vn1.m128i_u16[m] & 0x1 // If u16[m] == 0, result is 0
uint16 iIsEvenFloor = iIsOddFloor^0x1 // Flip 1 to 0, 0 to 1
// This line could surely perform an SSE2 multiply across multiple registers
vnPxChroma.m128i_u16[m] = iIsEvenFloor * vnPxCeilChroma.m128i_u16[m] +
iIsOddFloor * vnPxFloorChroma.m128i_u16[m]
В этом разделе коды вы опубликовали, и мою модификацию, мы все еще не в полной мере использовать/2/3 встроенные функции SSE1 но это может дать некоторые моменты о том, как это можно сделать (как векторизовать код).
Наконец, я бы сказал, чтобы проверить все. Выполните вышеуказанный код без изменений и профайл, прежде чем делать изменения и профилировать снова.Фактические показатели производительности могут вас удивить!
Update 1:
Я был через Intel SIMD Intrinsics documentation, чтобы выбрать соответствующие встроенные функции, которые могли бы быть использованы для этого. В частности, посмотрите на побитового XOR, AND и MULT/ADD
__m128 Типы данных
Тип данных __m128i может содержать шестнадцать 8-бит, восемь 16-бит, четыре 32-бит, или два 64 -битные целочисленные значения.
__m128i _mm_add_epi16 (__ m128i а, __m128i б)
Добавить 8 знаком или без знака 16-битных чисел в к 8 знаком или без знака 16-битных чисел в б
__m128i _mm_mulhi_epu16 (__ m128i , __m128i b)
Умножает 8 неподписанных 16-разрядных целых чисел от a на 8-значные 16-разрядные целые числа из b. пакеты верхние 16 битов в 8-беззнаковое 32-битных результатов
R0 = HIWORD (а0 * b0)
R1 = HIWORD (а1 * б1)
R2 = HIWORD (а2 * b2)
R3 = HIWORD (а3 * б3)
..
R 7 = HIWORD (а7 * b7)
__m128i _mm_mullo_epi16 (__ m128i а, __m128i б)
Умножает 8 знаком или без знака 16-битные целые числа от a 8-подписанным или неподписанным 16- битные целые числа из b. пакетов верхних 16 бит 8-знак или без знака 32-разрядных результаты
R0 = LOWORD (a0 * b0)
R1 = LOWORD (а1 * б1)
R2 = LOWORD (а2 * b2)
R3 = LOWORD (а3 * б3)
..
R 7 = LOWORD (а7 * b7)
__m128i _mm_and_si128 (__ m128i а, __m128i б)
Выполнить побитовое И 128-бит значение в m1 с 128-битным значением в м2.
__m128i _mm_andnot_si128 (__ m128i а, __m128i б)
Вычисляет побитовое И 128-битное значение в б и побитовое НЕ битового значения в 128- .
__m128i _mm_xor_si128 (__ m128i а, __m128i б)
Выполните побитового XOR из 128-битового значения в m1 с 128-битовым значением в м2.
ТАКЖЕ из вашего примера кода для справки
uint16 u1 = u2 = u3 ...= u15 = 0x1
__m128i vnMask = _mm_set1_epi16 (0x0001); // Устанавливает 8-значные 16-разрядные целочисленные значения.
uint16 VN1 [I] = vnFloors [I] & 0x1
__m128i VN1 = _mm_and_si128 (vnFloors, vnMask); // Вычисляет побитовое И 128-битного значения в a и 128-битное значение в b.
SSE встроенных функций, трудно читать. Не могли бы вы добавить несколько комментариев/эквивалентных блоков кода C++, чтобы объяснить этот раздел? –
Что вы хотите сделать для этого кода? – ronag
Я немного озадачен этим фрагментом (загадочными идентификаторами и без контекста), но почему бы вам не заменить сравнение с умножением и добавлением? – zrxq