如何优化循环?

时间:2010-10-21 11:40:25

标签: c++ optimization assembly intrinsics sse2

我有以下瓶颈功能。

typedef unsigned char byte;
void CompareArrays(const byte * p1Start, const byte * p1End, const byte * p2, byte * p3)
{
     const byte b1 = 128-30;
     const byte b2 = 128+30;
     for (const byte * p1 = p1Start; p1 != p1End; ++p1, ++p2, ++p3) {
        *p3 = (*p1 < *p2 ) ? b1 : b2;
    }
}

我想用SSE2内部函数替换C++代码。我尝试了_mm_cmpgt_epi8,但它使用了签名比较。我需要无符号比较。

是否有任何技巧(SSE,SSE2,SSSE3)来解决我的问题?

注意: 我不想在这种情况下使用多线程。

6 个答案:

答案 0 :(得分:9)

而不是抵消已签名的值以使其无符号,更有效的方法是执行以下操作:

  • 使用_mm_min_epu8获取p1的无符号最小值,p2
  • 使用_mm_cmpeq_epi8
  • 将此min与p2进行比较
  • 对于p1&lt;的元素,结果掩码现在为0x00对于p1> = p2
  • 的元素,p2和0xff
  • 您现在可以使用此_mm_or_si128_mm_andc_si128的掩码来选择合适的b1 / b2值

请注意,总共有4条指令,而使用offset + signed比较方法则为5条。

答案 1 :(得分:2)

您可以从数字中减去127,然后使用_mm_cmpgt_epi8

答案 2 :(得分:2)

是的,这可以在SIMD中完成,但制作蒙版需要几个步骤。

Ruslik说得对,我想。您希望使用0x80对每个组件进行xor以翻转有符号和无符号比较的意义。 _mm_xor_si128(PXOR)可以解决这个问题 - 在将掩码加载到SIMD寄存器之前,您需要在某处创建掩码作为静态char数组。然后_mm_cmpgt_epi8会为您提供一个掩码,您可以使用按位AND(例如_mm_and_si128)来执行屏蔽移动。

答案 3 :(得分:1)

是的,SSE不会在这里工作。 您可以使用OpenMP:

在多核计算机上提高此代码性能
void CompareArrays(const byte * p1Start, const byte * p1End, const byte * p2, byte * p3)
{
     const byte b1 = 128-30;
     const byte b2 = 128+30;

     int n = p1End - p1Start;
     #pragma omp parallel for
     for (int i = 0; i < n; ++p1, ++i) 
     {
        p3[i] = (p1[i] < p2[i]) ? b1 : b2;
     }
}

答案 4 :(得分:-1)

不幸的是,上面的许多答案都是不正确的。我们假设一个3位字:

无符号:4 5 6 7 0 1 2 3 ==签名:-4 -3 -2 -1 0 1 2 3(位:100 101 110 111 000 001 010 011)

Paul R的方法不正确。假设我们想知道3&gt; 2. min(3,2)== 2,这表示是,所以这个方法适用于此。现在假设我们想知道是否7> 2.签名表示中的值7为-1,因此min(-1,2)== -1,这表示7不大于2无符号。

安德烈的方法也不正确。假设我们想知道7&gt; 2,或a = 7,b = 2.符号表示中的值7为-1,因此第一项(a> b)失败,并且该方法表明7不大于2.

然而,由Alexey纠正的BJobnh的方法是正确的。只需从值中减去2 ^(n-1),其中n是位数。在这种情况下,我们将减去4以获得新的相应值:

旧签名:-4 -3 -2 -1 0 1 2 3 =&gt; new signed:0 1 2 3 -4 -3 -2 -1 == new unsigned 0 1 2 3 4 5 6 7。

换句话说,unsigned_greater_than(a,b)等同于signed_greater_than(a - 2 ^(n-1),b - 2 ^(n-1))。

答案 5 :(得分:-3)

使用pcmpeqb并成为你的力量。