2011-12-05 91 views
0

我在我的代码中有几个嵌套循环,我尝试在intel i7内核上使用intel SSE指令来加速应用程序。 该代码结构如下(val都在较高设置环路):嵌套for循环内的SSE指令

_m128 in1,in2,tmp1,tmp2,out; 
float arr[4] __attribute__ ((aligned(16))); 
val = ...; 

... several higher for loops ... 
for(f=0; f<=fend; f=f+4){ 
    index2 = ...; 
    for(i=0; i<iend; i++){ 
     for(j=0; j<jend; j++){ 
      inputval = ...; 
      index = ...; 
      if(f<fend-4){ 
       arr[0] = array[index]; 
       arr[1] = array[index+val]; 
       arr[2] = array[index+2*val]; 
       arr[3] = array[index+3*val]; 
       in1 = _mm_load_ps(arr); 
       in2 = _mm_set_ps1(inputval); 
       tmp1 = _mm_mul_ps(in1, in2); 
       tmp2 = _mm_loadu_ps(&array2[index2]); 
       out = _mm_add_ps(tmp1,tmp2); 
       _mm_storeu_ps(&array2[index2], out); 
      } else { 
       //if no 4 values available for SSE instruction execution execute serial code 
       for(int u = 0; u < fend-f; u++) array2[index2+u] += array[index+u*val] * inputval; 
      } 
     } 
    } 
} 

我认为有两个主要问题:用于从“阵列”对准值的缓冲液,而事实上,当没有4个值被留下(例如当fend = 6时,剩下的两个值应该用顺序码执行)。是否有任何其他方式加载in1的值和/或执行SSE intructions的3或2值?


感谢您的回答。加载和我想的一样好,但是在else语句中可以使用SSE指令解决的'剩余'部分是否有任何解决方法?

回答

1

您可以表达这一点:

  arr[0] = array[index]; 
      arr[1] = array[index+val]; 
      arr[2] = array[index+2*val]; 
      arr[3] = array[index+3*val]; 
      in1 = _mm_load_ps(arr); 

更简洁为:

  in1 = _mm_set_ps(array[index+3*val], array[index+2*val], array[index+val], array[index]); 

,摆脱arr,这可能给编译器的一些机会,优化掉一些多余的加载/存储。

但是,您的数据组织是主要问题,因为您几乎没有计算相对于加载和存储的数量,其中两个未对齐。如果可能的话,您需要重新组织数据结构,以便您可以在所有情况下一次加载和存储4个元素,从而形成对齐的连续内存,否则任何计算优势往往会被低效的内存访问模式所抵消。

+0

+1,因为你清楚地打败了我使用'_mm_set_ps'而不是对齐的缓冲区的解决方案。 – Mysticial

+0

谢谢 - 这些日子之前,我很难回答SIMD的问题 - 你有没有睡过? ;-) –

+0

我是一名学生,所以我的睡眠时间表由我的课程安排决定,与中西部地区的天气一样波动...... :) – Mysticial

2

我认为更大的问题是,有数据移动如此庞大的量如此之少的计算:

arr[0] = array[index];     // Data Movement 
arr[1] = array[index+val];    // Data Movement 
arr[2] = array[index+2*val];    // Data Movement 
arr[3] = array[index+3*val];    // Data Movement 
in1 = _mm_load_ps(arr);     // Data Movement 
in2 = _mm_set_ps1(inputval);   // Data Movement 
tmp1 = _mm_mul_ps(in1, in2);    // Computation 
tmp2 = _mm_loadu_ps(&array2[index2]); // Data Movement 
out = _mm_add_ps(tmp1,tmp2);   // Computation 
_mm_storeu_ps(&array2[index2], out);  // Data Movement 

虽然它“可能”有可能对此进行简化。我完全不相信,在这种情况下矢量化将会是有益的。

你必须改变你的数据布局,以避免跨步访问index + n*val

或者您可以等到2013年AVX2收集/分散指令可用?

+0

我知道数据移动的一部分是最大的问题,但仍然与我现在的应用程序从中受益的代码。我只是好奇,如果有其他方法可以获得更多的表现。 – Ricky

+1

Paul R的答案是我接下来要做的。你想要避免的主要事情就是你现在正在使用对齐的缓冲区。对于立即以不同的字大小重新访问最近存储的内存而言,这往往会有很大的代价。 – Mysticial

+0

谢谢,我会尝试! – Ricky

0

,如果你想充分受益形式SSE(因子4或超过最佳优化的代码来得更快,而不上证所明确的使用),你必须确保您的数据布局,这样你只能永远需要对齐加载和存储。尽管在你的代码片段中使用_mm_set_ps(w,z,y,x)可能会有所帮助,但你应该避免这种需要,即避免跨越访问(它们比单个_mm_load_ps效率低)。对于最后几个元素的问题,我通常确保我所有的数据不仅是16字节对齐的,而且数组的大小也是16个字节的倍数,这样我就不会有这样多余的剩余元素。当然,真正的问题可能有备用元素,但通常可以设置这些数据,使它们不会导致问题(设置为中性元素,即添加操作为零)。在极少数情况下,您只需要处理开始和/或结束于未对齐位置的数组子集。在这种情况下,可以使用按位运算(_mm_and_ps,_mm_or_ps)来抑制对不需要的元素的操作。