嵌套for循环中的SSE指令

时间:2011-12-05 09:54:24

标签: c++ intel sse

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

_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;
            }
        }
    }
}

我认为有两个主要问题:用于对齐'array'中的值的缓冲区,以及当没有剩下4个值时的事实(例如,当fend = 6时,剩下两个值应该用顺序代码)。是否还有其他方法可以从in1加载值和/或使用3或2值执行SSE intructions?


感谢目前为止的答案。加载和我认为的一样好,但是否可以使用SSE指令解决else语句中的“剩余”部分的解决方法?

3 个答案:

答案 0 :(得分: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

虽然它可能&#34;有可能简化这一点。在这种情况下,我根本不相信矢量化将会有益。

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

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

答案 1 :(得分: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个元素,形成对齐的连续内存,否则任何计算优势都会被低效的内存访问模式所抵消。

答案 2 :(得分:0)

如果您希望获得SSE的全部优势(比没有明确使用SSE的最佳优化代码快4倍或更多),您必须确保您的数据布局,以便您只需要对齐的负载和商店。虽然在代码片段中使用_mm_set_ps(w,z,y,x)可能会有所帮助,但您应该避免使用它,即避免跨步访问(它们的效率低于单个_mm_load_ps)。

至于最后几个&lt; 4元素的问题,我通常确保我的所有数据不仅仅是16字节对齐,而且数组大小是16字节的倍数,这样我就不会有这样的备用剩余元素。当然,真正的问题可能有备用元素,但通常可以设置该数据使得它们不会引起问题(设置为中性元素,即对于加法运算为零)。在极少数情况下,您只想处理以未对齐位置开始和/或结束的数组子集。在这种情况下,可以使用按位运算(_mm_and_ps,_mm_or_ps)来抑制对不需要的元素的操作。