2016-01-19 17 views
1

考慮下面的代碼段示出了一些簡單的算術運算gcc是否以代數方式優化C++代碼,如果是的話,程度如何?

int result = 0; 

    result = c * (a + b) + d * (a + b) + e; 

要獲得的結果在CPU上方的表達將需要執行兩個整數乘法和三個整數加法。但是,代數上面的表達式可以簡化爲下面的代碼。

result = (c + d) * (a + b) + e 

這兩個表達式在代數上是相同的,但第二個表達式只包含一個乘法和三個加法。 gcc(或其他編譯器)能夠自己做出這種簡單的優化。

現在假設編譯器足夠智能以進行此簡單優化,是否能夠優化更復雜的內容,如Trapezoidal rule(用於數值積分)。下面的示例近似sin(x)下的區域,其中0 <= x <= pi的步長爲pi/4(爲了簡單起見,小)。請假定所有文字都是運行時變量。

#include <math.h> 

// Please assume all literals are runtime variables. Have done it this way to 
// simplify the code. 
double integral = 0.5 * ((sin(0) + sin(M_PI/4) * (M_PI/4 - 0) + (sin(M_PI/4) + 
    sin(M_PI/2)) * (M_PI/2 - M_PI/4) + (sin(M_PI/2) + sin(3 * M_PI/4)) * 
    (3 * M_PI/4 - M_PI/2) + (sin(3 * M_PI/4) + sin(M_PI)) * (M_PI - 3 * M_PI/4)); 

現在上面的函數可以這樣寫,就像使用梯形法則一樣簡化了。這大大減少了獲得相同答案所需的乘法/除法的次數。

integral = 0.5 * (1/no_steps /* 4 in th case above*/) * 
    (M_PI - 0 /* Upper and lower limit*/) * (sin(0) + 2 * (sin(M_PI/4) + 
    sin(3 * M_PI/4)) + sin(M_PI)); 
+1

請提供指向C/C++語言規範的鏈接。在此之前,只有兩種不同的**語言C和C++。選一個!你的問題對於SO來說太廣泛了。請自己做一些研究。 gcc郵件列表可能是一個好的開始。或者你只是閱讀源代碼。 – Olaf

+1

您可以隨時編譯並檢查程序集以查看它的功能。 – NathanOliver

+1

小心!當你開始考慮整數溢出時,這種代數重排通常會中斷。我無法想到這個特定表達式的反例,但有很多表達式會打破它。 –

回答

7

GCC(和大多數C++編譯器,就此而言)不重構代數表達式。

這主要是因爲據GCC和一般的軟件算法而言,

double x = 0.5 * (4.6 + 6.7); 
double y = 0.5 * 4.6 + 0.5 * 6.7; 

assert(x == y); //Will probably fail! 

不保證該線是評估的確切數量相同。沒有這種保證,GCC不能優化這些結構。

此外,操作順序可能很重要。例如:

int x = y; 
int z = (y/16) * 16; 

assert(x == z); //Will only be true if y is a whole multiple of 16 

代數上,這兩行應該是等價的,對嗎?但如果yint,它實際上會做的是使x等於「y四捨五入到16的較低整數倍」。有時,這就是預期的行爲(就像你是字節對齊一樣)。其他時候,這是一個錯誤。重要的是,兩者都是有效的計算機代碼,並且兩者都可以根據具體情況而變得有用,並且如果GCC圍繞這些結構進行優化,則會妨礙程序員對代碼進行代理。

+0

在nathan oliver的建議下,我整理了上面的整數示例,並生成完全相同的程序集。在這種情況下,gcc通過重構'result = c *(a + b)+ d *(a + b)+ e;'result'=(c + d)*(a + b)+ e; '或者也許是我不知道的其他方式。然而,關於浮點示例,你是正確的。儘管如此,我不瞭解組裝,但無法弄清楚發生了什麼。 – silvergasp

0

是的,包含gcc的優化器會優化這種類型。不過,不一定是你引用的表達。您必須考慮到可能的溢出。例如,(a + a) - a可能會被優化爲a。可能的優化的另一個示例是a*a*a*atemp = a*a; (temp)*(temp)

通過讀取輸出彙編代碼可以觀察給定的編譯器是否優化了引用的表達式。不,這種類型的優化默認情況下不會與浮點一起使用(除非優化器可以證明沒有準確性丟失)。見Are floating point operations in C associative?可以讓例如gcc做-fassociative-math這個選項。 在你自己的危險。