[英]Can floating point multiplication by zero be optimised at runtime?
我正在編寫一個算法來查找nxn矩陣的逆。 讓我們來看一個3x3矩陣的具體情況。
手動反轉矩陣時,通常會查找包含一個或多個零的行/列,以便更快地執行行列式計算,因為它會消除您需要計算的項。
在C / C ++中遵循此邏輯,如果您標識具有一個或多個零的行/列,則最終將得到以下代碼:
float term1 = currentElement * DetOf2x2(...);
// ^
// This is equal to 0.
//
// float term2 = ... and so on.
由於編譯器無法知道currentElement
在編譯時將為零,因此無法將其優化為float term = 0;
因此浮點乘法將在運行時執行。
我的問題是,這些零值是否會使浮點乘法更快,或者無論currentElement
的值如何,乘法都會占用相同的時間量? 如果無法在運行時優化乘法,那么我可以刪除搜索包含零的行/列的邏輯。
除非計算是trival(例如所有常量),否則不允許編譯器優化它。
原因是,DetOf2x2可能返回NAN浮點值。 將NAN與零相乘不會返回零,而是再次返回NAN。
您可以在此處使用此小測試自行嘗試:
int main (int argc, char **args)
{
// generate a NAN
float a = sqrt (-1);
// Multiply NAN with zero..
float b = 0*a;
// this should *not* output zero
printf ("%f\n", b);
}
如果要優化代碼,則必須自行測試零。 編譯器不會為您執行此操作。
float term1 = currentElement * DetOf2x2(...);
即使currentElement為0,編譯器也會調用DetOf2x2(...)
:這肯定比最終的乘法要DetOf2x2(...)
得多,無論是否為0。 原因有很多:
DetOf2x2(...)
可能有副作用(如輸出到日志文件),即使currentElement
為0
也需要發生這種副作用, DetOf2x2(...)
可以返回值,無論如何都應傳播到term1
的非數字/ NaN標記(如Nils Pipenbrinck首先提到的那樣) 鑒於DetOf2x2(...)
幾乎肯定會處理只能在運行時確定的值,后者的可能性不能在編譯時排除。
如果你想避免調用Detof2x2(...)
,請嘗試:
float term1 = (currentElement != 0) ? currentElement * DetOf2x2(...) : 0;
現代的CPU會比實際的分支處理乘法由零速度非常快,快於一般的乘法,並更迅速。 甚至不打算嘗試優化它,除非零將通過至少幾十個指令傳播。
在運行時執行的優化稱為JIT(即時)優化。 在翻譯(編譯)時執行的優化稱為AOT(提前)優化。 你指的是JIT優化。 編譯器可能會在您的機器代碼中引入JIT優化,但它實現的優化要比常見的AOT優化要復雜得多。 優化通常基於重要性來實現,並且這種“優化”可能被視為負面地影響其他算法。 C實現不需要執行任何這些優化。
您可以手動提供優化,這將是“搜索包含零的行/列的邏輯”,或者類似這樣的內容: float term1 = currentElement != 0 ? currentElement * DetOf2x2(...) : 0;
float term1 = currentElement != 0 ? currentElement * DetOf2x2(...) : 0;
當編譯器可以猜測“currentElement”的值時,以下構造在編譯時有效。
float term1 = currentElement? currentElement * DetOf2x2(...):0;
如果在編譯時無法猜到,它將在運行時進行檢查,性能取決於處理器體系結構:分支之間的權衡(包括分支延遲和重建指令管道的延遲可達10或20個周期)和平坦代碼(一些處理器每個周期運行3個指令)和硬件分支預測(當硬件支持分支預測時)。
由於乘法吞吐量在x86_64處理器上接近1個周期,因此根據操作數值(如0.0,1.0,2.0或12345678.99)不存在性能差異。 如果存在這樣的差異,那將被視為加密式軟件中的隱蔽通道。
GCC允許在編譯時檢查函數參數
內聯浮動myFn(float currentElement,myMatrix M)
{
#if __builtin_constant_p(currentElement)&& currentElement == 0.0
返回0.0;
#其他
return currentElement * det(M);
#萬一
}
您需要在編譯器中啟用內聯和過程間優化。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.