2010-11-13 27 views
11

我正在使用SSE內在函數優化用於Intel x86 Nehalem微體系結構的一些代碼。使用SSE內在函數將4點產品存儲到C中的連續數組中的最有效方法

我的程序的一部分計算4點積,並將每個結果添加到數組的連續塊中的先前值。更具體地講,

tmp0 = _mm_dp_ps(A_0m, B_0m, 0xF1); 
tmp1 = _mm_dp_ps(A_1m, B_0m, 0xF2); 
tmp2 = _mm_dp_ps(A_2m, B_0m, 0xF4); 
tmp3 = _mm_dp_ps(A_3m, B_0m, 0xF8); 

tmp0 = _mm_add_ps(tmp0, tmp1); 
tmp0 = _mm_add_ps(tmp0, tmp2); 
tmp0 = _mm_add_ps(tmp0, tmp3); 
tmp0 = _mm_add_ps(tmp0, C_0n); 

_mm_storeu_ps(C_2, tmp0); 

請注意,我用4個臨時XMM要對這個寄存器來保存每個點積的結果。在每個xmm寄存器中,該結果被置入一個唯一的32個比特相對於另一個臨時XMM寄存器,使得最終結果看起來像這樣:

TMP0 = R0-零零零

TMP1 =零-R1-零零

TMP2 =零零-R2-零

TMP3 =零零零R3

我通過包含在每個變量TMP的值組合成一個XMM可變請按照以下說明對其進行總結:

tmp0 = _mm_add_ps(tmp0, tmp1); 
tmp0 = _mm_add_ps(tmp0, tmp2); 
tmp0 = _mm_add_ps(tmp0, tmp3); 

最後,我添加含所有4個結果的點的產品,以陣列的連續的部分,使得該陣列的索引是由點積增加,像這樣的寄存器(C_0n是4個值目前在要更新的數組; C_2是指向這4個值)地址:我想知道是否有采取點產品的結果,並把它們添加到的連續的大塊不太迂迴的,更有效的方式

tmp0 = _mm_add_ps(tmp0, C_0n); 
_mm_storeu_ps(C_2, tmp0); 

陣列。這樣,我在兩個只有1個非零值的寄存器之間進行了3次添加。似乎應該有一個更有效的方法來解決這個問題。

我感謝所有幫助。謝謝。

回答

6

對於這樣的代碼,我喜歡存儲A和B的「轉置」,以便{A_0m.x,A_1m.x,A_2m.x,A_3m.x}存儲在一個向量中等。然後,您可以使用乘積和增加來完成點積,當完成後,您可以將一個向量中的所有4點產品都不帶任何混洗。

這經常用於光線追蹤,以便一次對着一個平面測試4條光線(例如,當遍歷kd-tree時)。但是,如果您無法控制輸入數據,那麼執行轉置的開銷可能不值得。該代碼也將運行在SSE4之前的機器上,儘管這可能不是問題。


現有一小段代碼效率注:代替這個

tmp0 = _mm_add_ps(tmp0, tmp1); 
tmp0 = _mm_add_ps(tmp0, tmp2); 
tmp0 = _mm_add_ps(tmp0, tmp3); 
tmp0 = _mm_add_ps(tmp0, C_0n); 

可能稍微好一點的要做到這一點:

tmp0 = _mm_add_ps(tmp0, tmp1); // 0 + 1 -> 0 
tmp2 = _mm_add_ps(tmp2, tmp3); // 2 + 3 -> 2 
tmp0 = _mm_add_ps(tmp0, tmp2); // 0 + 2 -> 0 
tmp0 = _mm_add_ps(tmp0, C_0n); 

由於前兩個mm_add_ps的是現在完全獨立。此外,我不知道添加與洗牌的相對時間,但可能稍微快一點。


希望有所幫助。

1

您可以嘗試將點積結果留在低位字中,並使用標量存儲操作_mm_store_ss將每個m128寄存器中的一個浮點數保存到數組的相應位置。 Nehalem的存儲緩衝區應該在同一行上累積連續寫入,並批量刷新到L1。

親方式做它是celion的轉置方法。 MSVC的_MM_TRANSPOSE4_PS宏將爲您做轉置。

+0

您仍然必須在存儲之前將舊值(C_0n)添加到每個點積。他們都是獨立的,所以它可能不會太慢​​,但它不會更漂亮:) – celion 2010-11-13 09:37:27

3

也可以使用SSE3哈德。結果比使用_dot_ps更快,在一些簡單的測試中。 這將返回可添加的4點產品。

static inline __m128 dot_p(const __m128 x, const __m128 y[4]) 
{ 
    __m128 z[4]; 

    z[0] = x * y[0]; 
    z[1] = x * y[1]; 
    z[2] = x * y[2]; 
    z[3] = x * y[3]; 
    z[0] = _mm_hadd_ps(z[0], z[1]); 
    z[2] = _mm_hadd_ps(z[2], z[3]); 
    z[0] = _mm_hadd_ps(z[0], z[2]); 

    return z[0]; 
} 
1

我意識到這個問題是舊的,但爲什麼使用_mm_add_ps呢?將其替換爲:

tmp0 = _mm_or_ps(tmp0, tmp1); 
tmp2 = _mm_or_ps(tmp2, tmp3); 
tmp0 = _mm_or_ps(tmp0, tmp2); 

您可能會隱藏一些_mm_dp_ps延遲。第一個_mm_or_ps不等待最後的2點產品,並且它是(快速)按位操作。最後:

_mm_storeu_ps(C_2, _mm_add_ps(tmp0, C_0)); 
相關問題