2009-07-18 79 views
25

十年或兩年前,寫數字代碼是值得的,以避免使用乘法和除法,而是使用加法和減法。一個很好的例子是使用forward differences來評估多項式曲線,而不是直接計算多項式。浮點加法相對於浮點乘法的相對速度是多少

這仍然是這種情況,或者現代計算機體系結構先進到*,/不再比+慢多少倍, - ?具體而言,我對在現代典型x86芯片上運行的編譯C/C++代碼感興趣,這些代碼具有廣泛的板載浮點硬件,而不是試圖在軟件中執行FP的小型微代碼。我意識到流水線和其他架構增強功能會排除特定的週期數,但我仍然希望獲得有用的直覺。

回答

20

這也取決於指令組合。你的處理器將有幾個計算單元隨時待命,如果它們全部都被填滿,你將獲得最大的吞吐量。所以,執行一個循環的mul就像執行一個循環或增加一樣快 - 但是如果表達式變得更復雜,這個循環不成立。

例如,藉此循環:

for(int j=0;j<NUMITER;j++) { 
    for(int i=1;i<NUMEL;i++) { 
    bla += 2.1 + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; 
    } 
} 

爲NUMITER = 10^7,NUMEL = 10^2,初始化爲小的正數(NaN的是慢得多)兩個陣列,這需要使用6.0秒雙64位處理器。如果我有

bla += 2.1 * arr1[i] + arr2[i] + arr3[i] * arr4[i] ; 

更換循環只需要1.7秒......所以,因爲我們「過火」的補充,該MULS基本上免費的;減少添加物有所幫助。它得到的更加混亂:

bla += 2.1 + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; 

- 同MUL /加分配,但現在不斷的在增加,而不是相乘 - 需要3.7秒。您的處理器可能被優化爲更有效地執行典型的數值計算;所以像摩爾和縮比和的總和就像它的總和一樣好。加常數並不常見,因此速度較慢...

bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; /*someval == 2.1*/ 

再次需要1.7秒。

bla += someval + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; /*someval == 2.1*/ 

(與初始循環相同,但沒有昂貴的常數加法:2。1秒)

bla += someval * arr1[i] * arr2[i] * arr3[i] * arr4[i] ; /*someval == 2.1*/ 

(大多MULS,但是一個另外1.9秒爲單位)

所以,基本上;很難說哪個更快,但是如果你想避免瓶頸,更重要的是要有一個健全的組合,避免NaN或INF,避免添加常量。不管你做什麼,都要確保你測試並測試各種編譯器設置,因爲通常很小的更改只能改變它們。

一些更多的情況:

bla *= someval; // someval very near 1.0; takes 2.1 seconds 
bla *= arr1[i] ;// arr1[i] all very near 1.0; takes 66(!) seconds 
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; // 1.6 seconds 
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, 2.2 seconds 
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, floats 2.2 seconds 
bla += someval * arr1[i]* arr2[i];// 0.9 in x64, 1.6 in x86 
bla += someval * arr1[i];// 0.55 in x64, 0.8 in x86 
bla += arr1[i] * arr2[i];// 0.8 in x64, 0.8 in x86, 0.95 in CLR+x64, 0.8 in CLR+x86 
1

我找不到明確的參考,但廣泛的實驗告訴我,現在的浮動乘法與加法和減法的速度大致相同,而除法不是(但不是「多次」慢)。您只需運行自己的實驗即可獲得您想要的直覺 - 請記住提前生成隨機數(數百萬個),在開始計時之前先閱讀它們,並使用CPU的性能計數器(不要運行其他進程,就像你可以阻止它們一樣)來進行精確的測量!

-1

乘法和加法的時間差別可能很小。另一方面,由於它的遞歸性質,乘法運算仍然顯着慢於乘法運算。 關於現代x86架構sse指令在做浮點運算時應該考慮,而不是使用fpu。雖然一個好的C/C++編譯器應該給你使用sse而不是fpu的選項。

1

*/vs + - 的速度差異取決於您的處理器架構。一般來說,尤其是x86,現代處理器的速度差異已經變小。 *如有疑問,應該接近+,只是試驗。如果你有很多FP操作的難題,也可以考慮使用你的GPU(GeForce,...)作爲矢量處理器。

7

回答這個問題的最好方法是實際編寫一個你需要做的處理的基準/配置文件。經驗應儘可能超過理論。特別是當它很容易達到。

如果你已經知道你需要做數學的不同實現,你可以寫數學的幾個不同的代碼transfermations,看看你的表現峯。這將允許處理器/編譯器生成不同的執行流來填充處理器管線,併爲您的答案提供具體答案。

如果你是在DIV/MUL/ADD/SUB類型說明,你甚至可以在一些內聯彙編折騰專門控制其執行這些指令的變體特別是表現的興趣。但是,您需要確保您保持多個執行單元的繁忙,以便了解系統的性能。

還做這樣的事情會讓您只需在其上運行同一程序來比較處理器的多種變體的性能,而且還可以讓你在主板的差異因素。

編輯:

的+基本架構 - 是相同的。所以他們在邏輯上採取相同的時間進行計算。 *另一方面,需要多層,通常由「全加器」構成以完成單個操作。這樣可以保證每個週期都可以發送一個*到管道,但是它的延遲比加/減電路要高。 fp /操作通常使用近似方法來實現,該方法隨着時間的推移迭代收斂於正確答案。這些類型的近似值通常通過乘法來實現。所以對於浮點數,通常可以假設除法需要更長的時間,因爲將乘法運算(這已經是大型電路和自身)「展開」到多個乘法器電路的流水線中是不切實際的。仍然通過測試來衡量給定系統的性能。

16

理論上的信息是在這裏:

Intel®64 and IA-32 Architectures Optimization Reference Manual, APPENDIX C INSTRUCTION LATENCY AND THROUGHPUT

對於他們列出每一個處理器,在FMUL的潛伏期是非常接近FADD或FDIV的。在一些較舊的處理器上,FDIV比這個速度慢2-3倍,而在較新的處理器上,它與FMUL相同。

注意事項:

  1. 其實我聯繫說,你不能依賴於現實生活中的這些數字,因爲該處理器將做它想使事情更快,如果它是正確的文件。

  2. 您的編譯器很有可能會決定使用浮點乘法/除法可用的許多新指令集之一。

  3. 這是一個複雜的文件,只是爲了讓編譯器編寫者閱讀,我可能會錯誤的。就像我不清楚爲什麼某些CPU的FDIV延遲數完全缺失。

+1

非常酷的文件。我認爲有一點仍然是一致的(而且這份文件顯示了這一點),那就是分裂的速度遠遠大於乘法,加法和減法。從本文看來,雙精度除法的延遲比乘法慢10倍。所以,例如,我相信調用x = y * 0.5應該比調用x = y/2更快。 – 2009-07-20 15:20:16

+0

@SteveWortham您能否指向頁面,您發現有關fdiv的信息比fmul慢10倍? – 0fnt 2012-05-11 06:05:12