我說這是正確的基礎上我的純和常量理解,但如果任何人有兩個的精確定義,請說出來。這得到棘手,因爲海灣合作委員會的文件沒有規定確切意味着什麼功能有「除返回值沒有影響」(用於純)或「不審覈除它們的參數的任何值」(對於常量)。顯然,所有功能都一些效果(它們使用處理器週期,存儲器修改)並檢查一些值(功能代碼,常數)。
「副作用」必須根據C編程語言的語義來定義,但我們可以根據這些屬性的目的來猜測GCC人員的意思,即啓用其他優化(至少這就是我所假設的)。
原諒我,如果下面的一些過於基本...
純函數可以參與公共子表達式消除。他們的特點是他們不修改環境,所以編譯器可以自由地調用更少的時間而不改變程序的語義。
z = f(x);
y = f(x);
變爲:
z = y = f(x);
或獲取完全消除,如果z
和y
未使用。
所以我最好的猜測是,「純」的工作定義爲「它可以在不改變程序的語義來調用次數的任何功能」。然而,函數調用可以不移動,例如,
size_t l = strlen(str); // strlen is pure
*some_ptr = '\0';
// Obviously, strlen can't be moved here...
const函數可以重新排序,因爲它們不依賴於動態環境。
// Assuming x and y not aliased, sin can be moved anywhere
*some_ptr = '\0';
double y = sin(x);
*other_ptr = '\0';
所以我最好的猜測是「常量」的工作定義爲「可以在任何時候被調用,而不改變程序的語義的任何功能」。然而,有一種危險:
__attribute__((const))
double big_math_func(double x, double theta, double iota)
{
static double table[512];
static bool initted = false;
if (!initted) {
...
initted = true;
}
...
return result;
}
因爲它是常量,編譯器可能重新排序...
pthread_mutex_lock(&mutex);
...
z = big_math_func(x, theta, iota);
...
pthread_mutex_unlock(&mutex);
// big_math_func might go here, if the compiler wants to
在這種情況下,它可以同時從兩個處理器叫做即使只出現這在代碼中的關鍵部分內。然後,處理器可能會決定推遲更改table
,更改爲initted
已經過了,這是個壞消息。你可以用內存障礙或pthread_once
來解決這個問題。
我不認爲這個bug會在x86上出現,我不認爲它出現在許多沒有多個物理處理器(不是核心)的系統上。所以它可以很好地工作很長時間,然後在雙插槽POWER計算機上突然失效。
結論:這些定義的優點是,他們說清楚編譯器允許什麼樣的變化在這些屬性,(我認爲)在GCC文檔有些模糊的情況下作出。缺點是不清楚這些是GCC團隊使用的定義。
例如,如果您看一下Haskell語言規範,您會發現純度更準確的定義,因爲純度對於Haskell語言非常重要。
編輯:我一直無法強迫GCC或鏘成移動跨越另一個函數調用一個孤__attribute__((const))
函數調用,但似乎完全有可能在未來,這樣的事情會發生。記得-fstrict-aliasing
成爲默認時,每個人突然在他們的程序中有更多的錯誤? 這就是那種讓我謹慎的東西。
在我看來,當你標記功能__attribute__((const))
,你是有希望的編譯器,函數調用的結果是相同的,無論當你的程序的執行過程中被調用時,只要參數是相同。
但是,我確實想出了一種將const函數移出臨界區的方法,儘管我這樣做的方式可能被稱爲「作弊」。
__attribute__((const))
extern int const_func(int x);
int func(int x)
{
int y1, y2;
y1 = const_func(x);
pthread_mutex_lock(&mutex);
y2 = const_func(x);
pthread_mutex_unlock(&mutex);
return y1 + y2;
}
編譯器把這個成以下代碼(從組件):
int func(int x)
{
int y;
y = const_func(x);
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
return y * 2;
}
請注意,這不會只__attribute__((pure))
發生,const
屬性,只有const
屬性觸發該行爲。如你所見,臨界區內的呼叫消失了。看起來相當武斷,先前的調用被保留了下來,而且我也不願意承認編譯器在未來的某個版本中不會做出關於保留哪個調用或是否可能將函數調用移到某處的不同決定其他完全。
結論2:仔細輪距,因爲如果你不知道你正在編譯器什麼承諾,編譯器的未來版本可能會讓你大吃一驚。
你確定這是個問題嗎?如果呼叫被優化了,那麼下次會創建數據。只要數據只能通過返回值訪問,這應該沒有問題。 – ughoavgfhw
'pure'是GCC特有的,但'const'不是。 –
'__attribute __((const))'也是一個gcc-ism,但非gcc編譯器更廣泛地支持... –