Nun gilt es, die Theorie in die Praxis umzusetzen. Ein kleines Programm soll dabei sukzessiv optimiert werden.
Das Programm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
int x= 0; int y= 0; void writing(){ x= 2000; y= 11; } void reading(){ std::cout << "y: " << y << " "; std::cout << "x: " << x << std::endl; } int main(){ std::thread thread1(writing); std::thread thread2(reading); thread1.join(); thread2.join(); } |
Das Programm ist denkbar einfach. Es besteht aus den zwei Threads thread1 und thread2. thread1 schreibt die Werte x und y. thread 2 liest die Werte x und y in umgekehrter Reihenfolge. Bei der sukzessiven Optimierung des Programmes stehen vor allem zwei Fragen im Vordergrund.
- Ist der Programmverlauf definiert? Das heißt, besitzt er keinen kritischen Wettlauf?
- Welche Ausgaben für x und y sind möglich?
Die Antwort auf die zwei Fragen werde ich als Tabelle vorstellen.
Eine Frage ist noch unbeantwortet. Was verstehe ich unter sukzessiver Optimierung? Ich verstehe unter der sukzessiven Optimierung des Programms, das ich versuche, das Programm immer schwächer im Sinne des C++-Speichermodells zu synchronisieren. Exemplarisch will ich gleich mit dem Programm loslegen.
Vollkommen unsynchronisiert
Das Programm besitzt zwei kritische Wettläufe und ist damit undefiniert. Weder ist der Zugriff auf die Variable x, noch auf die Variable y geschützt. Da das Programm undefiniertes Verhalten besitzt, ist jedes Ergebnis möglich. Im C++ Jargon bedeutet dies, eine Cruise Missile kann starten oder dein Rechner in Feuer aufgehen. Das habe ich zwar noch nie erlebt, aber ... .
Welche Ausgabe für x und y sind möglich. Dazu lässt sich natürlich keine Aussage machen.
Es ist nicht ganz so schlimm
Die bekannten Architekturen sichern zu, dass ein Zugriff auf eine int-Variable atomar ist. Dies gilt unter der Einschränkung, dass diese int-Variable natürlich aligned ist. Natürlich aligned heißt, dass auf ein 32-Bit System ein int-Variable eine Adresse besitzen muss, die durch 4 teilbar ist, auf einem 64-Bit System eine, die durch 8 teilbar ist. Warum sage ich das so explizit? Ganz einfach. Mit C++11 lässt sich das Alignment der Datentypen explizit anpassen.
Nochmals. Ich argumentiere nicht dafür, int als atomar Variablen aufzufassen. Ich argumentiere nur, dass die Compilerbauer in diesem Fall strengere Regeln garantieren als sie der C++-Standard fordert. Damit ist das Programm aber nicht mehr standardkonform.
Wie geht's weiter?
In den nächsten, meisten kurz und knackigen Artikel werde ich das Programm immer weiter optimieren. Die folgenden Abstufungen werden folgen:
- Locks
- atomare Variablen mit Sequenzieller Konsistenz
- atomare Variablen mit Acquire-Release-Semantik
- atomare Variablen mit Relaxed-Semantik
- volatile Variablen
Im nächsten Artikel geht es los mit Locks.
Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library". Hole dir dein E-Book. Unterstütze meinen Blog.
Weiterlesen...