2011-12-05 98 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)來抑制對不需要的元素的操作。