2010-10-16 9 views
5

EDITED und verfeinert meine Frage nach Johannes wertvolle AntwortIst volatile hier erforderlich?

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(); 
    } 
} 

So lassen Sie uns Synchronisation für eine Weile vergessen. Die Frage ist, ob b für volatil erklärt werden muss. Ich habe den Standard gelesen und kenne die formale Definition der flüchtigen Semantik (ich verstehe sie sogar fast, das Wort ist fast der Schlüssel). Aber lass uns ein wenig informell sein. Wenn der Compiler sieht, dass es in der Schleife keine Möglichkeit gibt, b zu ändern, dann, wenn b nicht flüchtig ist, kann er es optimieren und annehmen, dass es while(vb) entspricht. Die Frage ist, in diesem Fall ist pf selbst flüchtig, also darf der Compiler annehmen, dass sich b in der Schleife nicht ändert, selbst wenn b nicht flüchtig ist?

Bitte verzichten Sie auf Kommentare und Antworten, die den Stil dieses Codeabschnittes ansprechen, dies ist kein reales Beispiel, dies ist eine experimentelle theoretische Frage. Kommentare und Antworten, die, abgesehen von der Beantwortung meiner Frage, auch die Semantik der volatilen in größerem Detail ansprechen, die Sie meiner Meinung nach missverstanden haben, sind sehr willkommen.

Ich hoffe meine Frage ist klar. TIA

Schnitt einmal mehr:
was ist das?

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(); 
    } 
} 

Gibt es einen prinzipiellen Unterschied zwischen den beiden Programmen? Wenn ja, worin besteht der Unterschied?

+0

Ich weiß nicht sehr gut C. Wenn dieser Code auch gültig ist C (außer Bool, das ich glaube nicht in C vorhanden), bitte sagen Sie mir, dass ich das C-Tag auch der Frage hinzufügen –

+0

es ist gültig C, wenn Sie # ybungalobill

+0

bitte ersten Code deine Frage stellen und dann fragen. Es macht keinen Spaß, seine Antwort ständig zu ändern. Was dürfen die Threads ändern (unter der Voraussetzung einer korrekten Synchronisation)? Gibt es Mutexe um bestimmte Teile? (d. h. um den "assign-pf + call" -Teil?) –

Antwort

3

Die Frage ist, in diesem Fall ist pf selbst flüchtig, also darf der Compiler annehmen, dass b sich nicht in der Schleife ändert, selbst wenn b nicht flüchtig ist?

Es kann nicht, weil man sagen, dass pf könnte von den anderen Threads geändert werden, und dies ändert sich indirekt b wenn pf dann durch die while-Schleife aufgerufen wird. Während es theoretisch nicht erforderlich ist, b normal zu lesen, muss es in der Praxis gelesen werden, um zu bestimmen, ob es einen Kurzschluss geben sollte (wenn bfalse wird, muss es vb ein anderes Mal nicht lesen).


Antwort auf den zweiten Teil

In diesem Fall pf ist nicht mehr flüchtig, so dass die Compiler es loswerden können und sehen, dass f1 einen leeren Körper und f2 Sätze b auf false gesetzt. Es könnte main wie folgt optimiert

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

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

Antwort auf ältere Version

Eine Bedingung für den Compiler der Schleife weg zu optimieren erlaubt werden soll, dass die Schleife nicht oder modifizieren hat Zugang zu dem flüchtigen Objekt (Siehe [stmt.iter] p5 in n3126). Sie tun das hier, damit es den Loop nicht optimieren kann. In C++ 03 durfte ein Compiler selbst die nicht-flüchtige Version dieser Schleife nicht optimieren (Compiler haben es aber trotzdem gemacht).

Beachten Sie, dass eine weitere Bedingung für die Optimierung darin besteht, dass die Schleife keine Synchronisation oder atomare Operationen enthält. In einem Multithread-Programm sollte dies jedoch ohnehin vorhanden sein. Selbst wenn Sie das volatile loswerden, wenn Ihr Programm richtig codiert ist, denke ich nicht, dass der Compiler es vollständig weg optimieren kann.

+0

@Johannes: Ok, es kann die Schleife nicht in Ordnung optimieren, aber ist es erlaubt, b weg zu optimieren und NICHT seinen Wert als Schleifenbedingung zu lesen? I.e. es in eine Endlosschleife umwandeln? Um zu vermeiden, Endlosschleife zu sagen, sagen wir, es war while (b && some_other_volatile_bool). Darf der Compiler es in while (some_other_volatile_bool) konvertieren? –

+0

@Armen sicher, wenn 'b' nie geändert wird, dann erlaubt C++ 03 das vollständig. Es muss flüchtig sein, damit es nicht optimiert werden kann. –

+0

@Johannes: Bearbeitete meine Frage. Auch kann ich nicht sagen, dass ich mit der Antwort vollkommen zufrieden bin. Sie sehen, Ihre Antwort bedeutet auch, dass, wenn eine Schleife einen Aufruf über einen Pointer-to-Funktion enthält, der innerhalb der Schleife nach Benutzereingaben gesetzt ist, der Compiler annehmen kann, dass sich b nicht ändert, was ich nicht glauben will, dass es wahr ist . Sollte ich? –

0

flüchtig nur verletzt Sie, wenn Sie denken, Sie hätten von einer Optimierung profitieren können, die nicht getan werden kann oder wenn es etwas kommuniziert, das nicht wahr ist.

In Ihrem Fall haben Sie gesagt, dass diese Variablen von anderen Threads geändert werden können. Code lesen, das ist meine Annahme, wenn ich flüchtig sehe, also aus der Sicht eines Betreuers, das ist gut - es gibt mir zusätzliche Informationen (was wahr ist).

Ich weiß nicht, ob die Optimierungen es wert sind zu versuchen, zu retten, da Sie sagten, dies ist nicht der echte Code, aber wenn sie nicht sind, dann gibt es keine Gründe, volatile nicht zu verwenden.

Nicht flüchtig verwenden, wenn Sie zu falschem Verhalten führen sollen, da die Optimierungen die Bedeutung des Codes ändern.

Ich sorge mich über die Kodierung der Minutien des Standards und des Verhaltens Ihrer Compiler, da sich Dinge wie diese ändern können und selbst wenn sie nicht, ändert sich Ihr Code (was den Compiler beeinflussen könnte) - also, außer Sie suchen für Verbesserungen bei der Mikrooptimierung dieses spezifischen Codes würde ich es einfach flüchtig lassen.

2

Die genauen Anforderungen an volatile im aktuellen C++ - Standard in einem solchen Fall sind, wie ich es verstehe, vom Standard nicht ganz klar definiert, da sich der Standard nicht wirklich mit Multithreading befasst. Es ist im Grunde ein Compiler-Hinweis. Stattdessen werde ich auf das eingehen, was in einem typischen Compiler passiert.

Angenommen, der Compiler kompiliert Ihre Funktionen unabhängig voneinander und verknüpft sie dann miteinander. In beiden Beispielen haben Sie eine Schleife, in der Sie eine Variable überprüfen und einen Funktionszeiger aufrufen. Im Kontext dieser Funktion hat der Compiler keine Ahnung, was die Funktion hinter diesem Funktionszeiger tun wird, und er muss daher immer b aus dem Speicher nach dem Aufruf neu laden. Somit ist volatile dort irrelevant.

Erweiterung, die zu Ihrem ersten konkreten Fall, und so dass der Compiler Vollprogrammoptimierungen zu machen, weil pf flüchtig ist der Compiler noch keine Ahnung hat, was es geht um zu zeigen (es kann nicht einmal davon ausgehen, es ist entweder f1 oder f2!), und somit ebenfalls keine Annahmen darüber treffen können, was über den Funktionszeiger-Aufruf unverändert bleiben wird - und so ist volatile auf b immer noch irrelevant.

Ihr zweiter Fall ist eigentlich einfacher - vb in es ist ein Hering. Wenn Sie das eliminieren, können Sie sehen, dass selbst in einer vollständig single-threaded-Semantik der Funktionszeiger-Aufruf b ändern kann. Sie tun nichts mit undefiniertem Verhalten, und daher muss das Programm korrekt funktionieren, ohne volatile - denken Sie daran, dass volatile ein No-Op ist, wenn Sie keine Situation mit externen Thread-Optimierungen in Betracht ziehen. Daher, ohne vb im Bild, können Sie möglicherweise volatile nicht benötigen, und es ist ziemlich klar, dass das Hinzufügen von vb ändert nichts.

Also, zusammenfassend: Sie brauchen in keinem Fall volatile.Der Unterschied besteht darin, dass im ersten Fall, wenn fp nicht flüchtig waren, ein ausreichend fortgeschrittener Compiler möglicherweise b weg optimieren könnte, während es im zweiten Fall nicht einmal ohne Volatile irgendwo im Programm möglich ist. In der Praxis erwarte ich nicht, dass Compiler diese Optimierung tatsächlich vornehmen würden.

+0

In meinem zweiten Beispiel wird der Compiler in der Regel verstehen, dass pf entweder f1 oder f2 ist, analysieren beide Funktionen und sehen, dass b ändern kann, oder der bloße Aufruf eines Pointer-to-Funktion ist genug, davon zu verzichten Optimieren von Lesevorgängen von einer globalen Variablen? –

+0

Und übrigens, C++ 0x beschäftigt sich mit Multi-Threading ... viel –

+0

Richtig; Ich habe an den aktuellen C++ - Standard gedacht (und bearbeitet, um das klar zu machen). In jedem Fall wäre ich überrascht, wenn der Compiler im zweiten Beispiel eine Analyse durchführt, auf die der Funktionszeiger zeigen kann; Ich würde nicht erwarten, dass das im Allgemeinen eine produktive Analyse wäre (obwohl es in diesem kleinen Beispiel offensichtlich nützlich ist). –