2010-06-02 27 views
2

我一直在試圖找出如何在一個非常關鍵的幾行獲得在我的代碼一些改進:幫助組裝/ SSE乘

float x = a*b; 
float y = c*d; 
float z = e*f; 
float w = g*h; 

所有A,B,C ...是彩車。

我決定研究使用SSE,但似乎無法找到任何改善,實際上它變慢了兩倍。我SSE代碼:

Vector4 abcd, efgh, result; 
abcd = [float a, float b, float c, float d]; 
efgh = [float e, float f, float g, float h]; 
_asm { 
movups xmm1, abcd 
movups xmm2, efgh 
mulps xmm1, xmm2 
movups result, xmm1 
} 

我還使用標準的內聯彙編嘗試,但它不會出現,我可以收拾寄存器與4個浮動點像我可以與SSE。

任何意見,或幫助將不勝感激,我主要需要了解爲什麼我使用SSE的計算比串行C++代碼慢?

我正在編譯Visual Studio 2005,在Windows XP上,使用帶有HT的Pentium 4,如果它提供了任何其他信息來幫助。

在此先感謝!

+0

我認爲你需要提供更多的上下文。簡單地乘四對浮標將在任何現代PC上花費幾乎無法衡量的時間。這是在一個循環?你是否將結果存儲在某個地方或將它們用作下一次迭代的中間體? – 2010-06-02 21:04:06

+0

我也明白,從上交所的最大好處是可以做很多重複,裝箱每次註冊,但所有我打算使用此生成的編號做只是要返回到一些加減電話,沒有我想要包含在SSE代碼中,但計算時間的任何改進都會在代碼的整個生命週期中節省大量時間。 – Brett 2010-06-02 21:08:12

+0

這不一定是真的。如果這不是一個循環,那麼在任何給定的代碼中,任何好處都將是完全不明顯的。當然,如果軟件使用了幾千年,節省的總時間可能是有意義的,但這確實是關於它的。不要過度優化,今天的編譯器非常好。如果實際運行速度太慢,首先要進行分析,然後優化瓶頸。 – Donnie 2010-06-02 21:19:59

回答

3

正如你已經發現的,只是用SSE替換一些指令是行不通的,因爲你需要在內存中混洗數據以正確加載SSE寄存器,並且這些移動數據在內存中(構造數組的位)會因爲內存非常慢而拋棄性能(拋開硬盤,內存現在總是瓶頸)。

另外,如果不使用寫入RAM和讀取操作,則無法在SSE和FPU/ALU之間移動數據。現代的IA32芯片可以很好地應對這種特殊的模式(寫入然後讀取),但是仍然會使某些緩存失效,從而產生敲門效應。

爲了充分利用SSE,您需要查看整個算法和算法使用的數據。 a,b,c和d以及e,f,g和h的值需要在這些陣列中永久保存,以便在加載SSE寄存器之前在內存中沒有數據移位。這不是直截了當的,可能需要對代碼和數據進行大量修改(您可能需要以不同方式在磁盤上存儲數據)。

也許值得指出的是,SSE只有32位(或64位,如果你使用雙打),而FPU是80位(不管是浮點型還是雙精度型),所以當使用SSE和使用SSE時會有稍微不同的結果FPU。只有你知道這是否會成爲問題。

+0

根據我對您的答案的理解,聽起來像我應該嘗試利用內在函數,如果我可以將它們用於多個計算,這是正確的嗎?而這背後的推理是因爲我自己的數據移動效率不高?我無法將a,b,c和d以及e,f,g和h的值永久存儲在這些數組中,因爲它們需要爲每次計算加載當前值,因此我很難看到一個好處?謝謝你的幫助! – Brett 2010-06-03 12:20:28

+0

@Brett:是的,基本上就是這樣。你需要把所有東西都保存在SSE中才能真正獲得好處。在名爲SSE ​​ - Streaming SIMD Extensions中有一點線索。出於好奇,這些價值從何而來,即更大的圖景是什麼? – Skizz 2010-06-03 19:26:00

+0

所以,更大的圖像是它實際上是旋轉矩陣的一部分,但是我通過一個大循環在每次迭代中做一個旋轉矩陣,在那裏比較特徵向量。由於結構的原因,我沒有看到一次連接一系列SSE計算的可預見的方式,但即使有絲毫的益處,也肯定會對我的程序運行時間有很大的改進。或者,b,d,f,h是在初始化階段計算的正弦和餘弦值,我目前正在將它們存儲在對齊的塊中以便更快地進行乘法運算。 Thx爲您提供幫助! – Brett 2010-06-03 20:17:27

1

您可以在更新的VS版本和可能在2005年的程序選項中啓用SSE和SSE2。使用快速版編譯?

此外,您在SSE中的代碼可能會比較慢,因爲當您編譯串行C++時,編譯器非常聰明,並且使其非常快速 - 例如,可以在正確的時間自動將它們放入正確的寄存器中。例如,如果操作以串行方式發生,編譯器可以減少緩存和分頁的影響。然而,內聯彙編程序最多可以優化得很差,應儘可能避免。

此外,您必須爲SSE ​​/ 2執行大量工作才能帶來顯着的好處。

+0

我想我仍然困惑的是我得到了一些可用的SSE/2代碼(我曾經許多版本的代碼粘貼在上面),其實際上比我的串行代碼慢。足夠讓我的〜10秒的程序(完全串行寫)然後花費大約11.5秒(在SSE/2中只需要那個操作) – Brett 2010-06-03 12:14:17

+0

編譯器,學着去愛它。 :P – Puppy 2010-06-03 12:20:15

3

您正在使用未對齊的指令,這些指令非常慢。 你可能想要嘗試對齊你的數據,16字節的邊界,並使用movaps。 你更好的選擇是使用內在函數而不是程序集,因爲編譯器可以自由地根據需要來排列指令。

+0

所以,我測試了我認爲你在說什麼,通過使用movups命令來存儲在寄存器中對齊的值,然後使用movaps來模擬已對齊的數據,並且它最終比串行C++代碼更快因爲我在對齊數據後開始定時器。如果我總是從未對齊的數據開始,那麼對我而言,看不到SSE/ASM的好處是否有意義? – Brett 2010-06-03 12:17:06

1

這是一箇舊線程,但我注意到您的示例中有一個錯誤。如果要執行此:

float x = a*b; 
float y = c*d; 
float z = e*f; 
float w = g*h; 

然後代碼應該是這樣的:

Vector4 aceg, bdfh, result; // xyzw 
abcd = [float a, float c, float e, float g]; 
efgh = [float b, float d, float f, float h]; 
_asm { 
movups xmm1, abcd 
movups xmm2, efgh 
mulps xmm1, xmm2 
movups result, xmm1 
} 

,並獲得甚至一些更快的速度,我建議您不要使用一個單獨的寄存器爲「結果」。

對於初學者來說,並非所有算法都有利於在SSE中重寫。數據驅動的算法(如由查找表驅動的算法)不能很好地轉化爲SSE,因爲很多時間都會丟失數據並將數據解包到向量中以便SSE運行。

希望這仍然有幫助。

0

首先,當你有128bit(16byte)對齊的東西時,你應該使用MOVAPS,因爲它可以更快。 即使在32位系統上,編譯器通常也會給你16字節對齊。

您的C/C++行不會與您的sse代碼執行相同的操作。

一個xmm寄存器中的四個浮點數乘以另一個寄存器中的四個浮點數。 給你:

float x = a*e; 
float y = b*f; 
float z = c*g; 
float w = d*h; 

在SSE1你必須使用SHUFPS乘以之前重新排序兩個寄存器的浮動。

同樣爲了處理大於cpu緩存的數據,您可以使用非臨時存儲(MOVNTPS)來減少緩存污染。 請注意,在其他情況下,非暫時性商店速度較慢。