2010-10-16 38 views
5

編輯和後約翰內斯寶貴的回答,改進我的問題這裏是不穩定的嗎?

bool b = true; 
volatile bool vb = true;  
void f1() { } 
void f2() { b = false; } 

void(* volatile pf)() = &f1; //a volatile pointer to function 

int main() 
{ 
    //different threads start here, some of which may change pf 
    while(b && vb) 
    { 
     pf(); 
    } 
} 

所以,讓我們忘了同步一會兒。問題是b是否必須被宣佈爲不穩定。我已閱讀標準和排序 - 知道易失性語義的正式定義(我甚至幾乎理解它們,這個詞幾乎就是關鍵)。但是,讓我們在這裏有點不正式。如果編譯器在循環中看到b沒有辦法改變,那麼除非b是易失性的,它可以優化它,並假定它等於while(vb)。問題是,在這種情況下,pf本身是不穩定的,那麼編譯器是否允許假設b在循環中不會改變,即使b不是易失性的?

請不要評論和解答這段代碼的風格,這不是一個現實世界的例子,這是一個實驗性的理論問題。 評論和回答,除了回答我的問題,還解決更詳細的揮發性的語義,你認爲我誤解了非常受歡迎。

我希望我的問題很清楚。 TIA

編輯一次:
這個怎麼樣?

bool b = true; 
volatile bool vb = true; 
void f1() {} 
void f2() {b = false;} 
void (*pf)() = &f1; 

#include <iosrteam> 
int main() 
{ 
    //threads here 

    while(b && vb) 
    { 
     int x; 
     std::cin >> x; 
     if(x == 0) 
     pf = &f1; 
     else 
     pf = &f2;  
     pf(); 
    } 
} 

這兩個程序之間有一個主要的區別。如果是,有什麼區別?

+0

我不知道C很好。如果此代碼也是有效的C(除了bool,我認爲它不存在於C中),請告訴我,以便我將C標記添加到問題中 – 2010-10-16 17:37:15

+0

它是有效的C如果您#include ybungalobill 2010-10-16 17:58:25

+0

請先寫代碼提出你的問題,然後問。不斷改變自己的答案是沒有意義的。此外,允許更改哪些線程(假設正確同步)?某些部分是否存在互斥體? (即圍繞「assign-pf + call」部分?) – 2010-10-16 18:24:00

回答

3

問題是,在這種情況下,pf本身就是易失性的,那麼編譯器是否允許假設b在循環中不會改變,即使b不是易失性的?

這不可能,因爲你說pf可能被其他線程改變,而這種改變間接如果bpf由while循環中調用即可。因此,雖然理論上不需要正常讀取b,但實際上必須讀取它以確定其是否應該短路(當b變成false時,它不能再讀取vb)。


回答第二個部分

在這種情況下pf不揮發了,所以編譯器可以擺脫它,看到f1有一個空的身體和f2b爲false。它可以優化main如下

int main() 
{ 
    // threads here (which you say can only change "vb") 

    while(vb) 
    { 
     int x; 
     std::cin >> x; 
     if(x != 0) 
     break;  
    } 
} 

答到舊版本的編譯器被允許優化環路走

一個條件是,循環就不會訪問或修改任何揮發性對象(參見n3126中的[stmt.iter] p5)。你這樣做,所以它不能優化循環。在C++ 03中,編譯器不允許對該循環的非易失性版本進行優化(但編譯器無論如何都是這樣做的)。

請注意,能夠優化它的另一個條件是該循環不包含同步或原子操作。無論如何,在多線程程序中,這應該是存在的。所以,即使你擺脫了volatile,如果你的程序編碼正確,我不認爲編譯器可以完全優化它。如果你認爲你可能會從能不能做到優化受益

+0

@Johannes:好吧,它不能優化循環,但它是否允許優化b而不讀取它的值作爲循環條件?即將其轉換爲無限循環?爲了避免說無限循環,讓我們假設它是(b && some_other_volatile_bool)。編譯器是否允許將其轉換爲while(some_other_volatile_bool)? – 2010-10-16 17:43:17

+0

@Armen當然,如果'b'永遠不會改變,那麼C++ 03完全允許這樣做。它必須是易變的,因爲它不能優化它。 – 2010-10-16 17:44:22

+0

@Johannes:編輯我的問題。我也不能說我對答案完全滿意。你看,你的答案也意味着如果一個循環包含一個通過指向函數的調用,而這個指針是根據用戶輸入在循環內部設置的,那麼編譯器可以假設b不會改變,我不想相信這是真的。我是不是該? – 2010-10-16 17:48:59

0

揮發性只傷害了你,或者如果它傳達的東西是不正確的。

就你而言,你說這些變量可以被其他線程改變。閱讀代碼時,這是我看到不穩定時的假設,所以從維護者的角度來看,這很好 - 它給了我額外的信息(這是真的)。

我不知道是否優化是值得嘗試的,因爲你說這是不是真正的代碼打撈,但如果他們不那麼有沒有任何理由不使用易揮發。

時,你應該在不正確的行爲的結果,因爲在優化正在改變代碼的含義不使用易揮發。

我擔心編碼標準和編譯器行爲的細節,因爲這樣的事情可能會改變,即使它們不改變,代碼也會改變(這可能會影響編譯器) - 所以,除非你正在尋找對於這個特定代碼的微觀優化改進,我只是讓它變得不穩定。

2

在這樣的情況下,在當前C++標準中關於volatile的確切要求,據我所知,並不完全由標準定義,因爲標準並沒有真正處理多線程。它基本上是一個編譯器提示。相反,我將解決典型編譯器中發生的情況。

首先,假設編譯器正在獨立編譯你的函數,然後將它們連接在一起。在任何一個例子中,你都有一個循環來檢查一個變量,並調用一個函數指針。在該函數的上下文中,編譯器不知道該函數指針後面的函數會做什麼,因此在調用它之後必須始終從內存中重新加載b。因此,volatile在那裏是不相關的。

擴展,爲你的第一個實際案例,並允許編譯器使整個程序優化,因爲pf是揮發性的編譯器仍然不知道它會在被指向(它甚至不能承擔它要麼f1f2!),因此也不能對函數指針調用中未修改的內容做任何假設 - 所以volatileb上仍然不相關。

你的第二個案例實際上是簡單 - vb它是一個紅色的鯡魚。如果消除這種情況,即使在完全單線程語義中,也可以看到函數指針調用可能會修改b。你沒有做任何未定義的行爲,所以程序必須正確運行,而不需要volatile - 記住,如果你不考慮外部線程調整的情況,volatile是沒有操作的。因此,如果圖片中沒有vb,則不可能需要volatile,而且很明顯,添加vb不會發生任何變化。

因此,在總結:你不會在任何情況下需要volatile。不同之處在於,在第一種情況下,如果fp不是易失性的,那麼足夠先進的編譯器可能會優化b,而在第二種情況下甚至不能在程序中的任何地方不發生變化。在實踐中,我不希望任何編譯器會真正地進行優化。

+0

在我的第二個例子中,編譯器通常會理解pf是f1還是f2,分析這兩個函數並且看到b可以改變,或者僅僅調用一個指向函數的指針就足以讓它避免從全局變量中優化讀取? – 2010-10-16 20:12:40

+0

而且,順便說一句,C++ 0x確實處理多線程......很多 – 2010-10-16 20:14:05

+0

正確;我正在考慮當前的C++標準(並進行了編輯以清楚地說明)。無論如何,如果在第二個例子中編譯器對函數指針可以指向什麼進行分析,我會感到驚訝;我不會期望這將是一個有效的分析(儘管它在這個小例子中顯然是有用的)。 – 2010-10-17 07:06:58