我的代码中有几个嵌套的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语句中的“剩余”部分的解决方法?
答案 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)来抑制对不需要的元素的操作。