SSE累计求和

Dan*_*cik 3 c++ performance sse

我有一个简单的问题.具有起始uint_32值(比如125)和要添加的__m128i操作数,例如(+ 5,+ 10,-1,-5).我希望尽可能快地得到一个向量(125 + 5,125 + 5 + 10,125 + 5 + 10 - 1,125 + 5 + 10 - 1 - 5),即从操作数中累加值到起始值.到目前为止,我能想到的唯一解决方案是添加4个__m128i变量.例如,他们会

/* pseudoSSE code... */
__m128i src =     (125,125,125,125)
__m128i operands =(5,10,-1,-5)

/*  Here I omit the partitioning of operands into add1,..add4 for brevity  */

__m128i add1 =    (+05,+05,+05,+05)
__m128i add2 =    (+00,+10,+10,+10)
__m128i add3 =    (+00,+00,-01,-01)
__m128i add4 =    (+00,+00,+00,-05)
__m128i res1 = _mm_add_epu32( add1, add2 )
__m128i res2 = _mm_add_epu32( add3, add4 )
__m128i res3 = _mm_add_epu32( res1, add2 )
__m128i res  = _mm_add_epu32( res3, src  )
Run Code Online (Sandbox Code Playgroud)

像这样,我得到了我想要的东西.对于此解决方案,我将需要设置所有add_变量,然后执行4次添加.我真正想问的是,这是否可以更快完成.要么通过一些不同的算法,要么使用一些我还不知道的专门的SSE函数(类似于_mm_cumulative_sum()).非常感谢.

Evg*_*uev 5

您可以添加更多并行性并使用3个添加而不是4:

const __m128i src = _mm_set1_epi32(125);
const __m128i operands = _mm_set_epi32(5,10,-1,-5);

const __m128i shift1 =
  _mm_add_epi32(operands,
    _mm_and_si128(_mm_shuffle_epi32(operands, 0xF9),
                  _mm_set_epi32(0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF)));

const __m128i shift2 =
  _mm_add_epi32(shift1,
    _mm_and_si128(_mm_shuffle_epi32(shift1, 0xFE),
                  _mm_set_epi32(0,0,0xFFFFFFFF,0xFFFFFFFF)));

const __m128i res = _mm_add_epi32(src, shift2);
Run Code Online (Sandbox Code Playgroud)

这里使用SSE2指令集.使用较新的指令集,您可以使用_mm_shuffle_epi8等单个指令替换_mm_and_si128/_mm_shuffle_epi32.

累计总和计算为2个添加项,如下所示:

   a    b    c    d
 +      a    b    c
  ------------------
   a   a+b  b+c  c+d
 +           a   a+b
  ------------------
   a   a+b a+b+c a+b+c+d
Run Code Online (Sandbox Code Playgroud)

SSE不适合这样的任务.它的性能仅适用于"垂直"操作,但它需要大量额外的"水平"操作工作,这里需要它.

  • @DanBencik:如果关闭优化,则可能会损害SSE和非SSE功能的效率。因此,不进行优化就测试性能不是一个好主意。要告诉编译器不要剪切“非生产性”代码,您可以将计算结果的一部分累加到一个变量中,然后将其打印到控制台,或将其发送到其他一些外部函数,或写入volatile变量。 (2认同)