好的,我不知道这段代码在做什么,但我知道你想要优化三元运算符并使这部分代码只在SSE中运行。作为第一步,我建议尝试使用整数标志和乘法来避免条件运算符。例如:
这个部分
for(int m=0
{
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])
}
在语法上等同于这个
for(int m=0; m < PBS_SSE_PIXELS_PROCESS_AT_ONCE; m+=2)
for(int m=1; m < PBS_SSE_PIXELS_PROCESS_AT_ONCE; m+=2)
将其分成两个循环后,您失去了串行内存访问的性能提升,但可以省去模运算和两个条件运算符。
现在您可能会注意到每个循环中有两个布尔运算符以及乘法。 我要补充的是,这些乘法不是SSE内在实现。 您的vn1.m123i_u16 []数组中存储了什么? 它只包含0和1吗?
如果是,则不需要此部分,并且可以将其删除。 如果不是,请将此数组中的数据归一化为仅包含零和一。 如果vn1.m123i_u16数组仅包含1和0,则该代码变为
uint16 iIsOddFloor = vn1.m128i_u16[m]
uint16 iIsEvenFloor = iIsOddFloor ^ 0x1 // Flip 1 to 0, 0 to 1
您还会注意到,我没有使用SSE乘法来执行isEvenFloor * vnPx...部分
,也没有使用SSE寄存器来存储iIsEvenFloor
和iIsOddFloor
。很抱歉我无法记住u16乘法/寄存器的SSE内在函数,但无论如何,我希望这种方法对您有所帮助。您应该考虑一些优化:
// 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]
在你所发布的代码和我的修改中,我们仍未充分利用SSE1/2/3内嵌函数,但这可能提供了一些关于如何向量化代码的思路。
最后我想说要测试所有东西。在进行更改和再次进行剖析之前运行上面的代码未经修改。实际的性能数字可能会让你感到惊讶!
更新1:
我已经查看了Intel SIMD Intrinsics文档,挑选出了与此相关的内在函数,这些函数可能对此有用。特别是要看一下按位异或、按位与和MULT/ADD。
__m128 数据类型
__m128i 数据类型可以容纳16个8位、8个16位、4个32位或2个64位整数值。
__m128i _mm_add_epi16(__m128i a, __m128i b)
将a中的8个有符号或无符号16位整数加到b中的8个有符号或无符号16位整数。
__m128i _mm_mulhi_epu16(__m128i a, __m128i b)
将a中的8个无符号16位整数乘以b中的8个无符号16位整数。
打包8个无符号32位结果的上16位。
R0=hiword(a0 * b0)
R1=hiword(a1 * b1)
R2=hiword(a2 * b2)
R3=hiword(a3 * b3)
..
R7=hiword(a7 * b7)
__m128i _mm_mullo_epi16(__m128i a, __m128i b)
将a中的8个有符号或无符号16位整数乘以b中的8个有符号或无符号16位整数。
打包8个有符号或无符号32位结果的上16位。
R0=loword(a0 * b0)
R1=loword(a1 * b1)
R2=loword(a2 * b2)
R3=loword(a3 * b3)
..
R7=loword(a7 * b7)
__m128i _mm_and_si128(__m128i a, __m128i b)
对m1中的128位值执行按位AND运算,与m2中的128位值。
__m128i _mm_andnot_si128(__m128i a, __m128i b)
计算b中的128位值和a中的128位值的按位NOT的按位AND。
__m128i _mm_xor_si128(__m128i a, __m128i b)
对m1中的128位值执行按位异或运算,与m2中的128位值。
也可以参考您的代码示例
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); //计算a中的128位值和b中的128位值的按位AND。