Laden...

Thread.MemoryBarrier() und Cache-Optimierungen

Erstellt von bSharp vor 13 Jahren Letzter Beitrag vor 13 Jahren 1.216 Views
B
bSharp Themenstarter:in
48 Beiträge seit 2010
vor 13 Jahren
Thread.MemoryBarrier() und Cache-Optimierungen

Hallo,

in einem Buch geht es gerade um Nichtblockierende Synchronisation.

Folgende Funktion, ein Beispiel aus dem Buch, terminiert nicht (Release-Modus und Codeoptimierung wählen):


private static void forever()
{
	bool complete = false;
	var t = new Thread(() =>
	{
	bool toggle = false;
	while (!complete)
	{
              toggle = !toggle;
	}
	});
            
	t.Start();
            
	Thread.Sleep(1000);
	complete = true;

	t.Join();
}

Der Grund ist einfach, dass die complete-Variable im CPU-Register gecachet wird und der Thread t niemals die Zuweisung =true sieht.

Nun werden zwei Lösungen besprochen: Einmal volatile für die complete-Variable und einmal MemoryBarrier.

Bei letzterem wird vorgeschlagen ein Thread.MemoryBarrier() in die while-Schleife zu packen. Es funktioniert, aber ich verstehe nicht ganz warum.

Ich dachte die MemoryBarrier verhindert nur Neuanordnungen von Instruktionen auf CPU, CLR oder Compiler-Level. In der MSDN (*) heißt es auch:
"Synchronisiert den Speicherzugriff wie folgt: Der Prozessor, der den aktuellen Thread ausführt, kann Anweisungen nicht so neu anordnen, dass Speicherzugriffe vor dem Aufruf von MemoryBarrier nach Speicherzugriffen ausgeführt werden, die nach dem Aufruf MemoryBarrier erfolgen."

Kein Wort über verhinderte Register- und Cache-Optimierungen, was zur Lösung des obigen Problems (ähnlich dem volatile Schlüsselwort) notwendig wäre, oder?

Meine Frage also: Weiß jemand wie sich Thread.MemoryBarrier() auf Cache-Optimierungen auswirkt?

(*) Die MSDN enthält sowieso Fehler zur MemoryBarrier, da der Beispielcode entgegen dem Hinweis auch auf anderen CPUs nicht terminiert

MfG

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

guck mal Threading in C# - Part 4 - Advanced Threading. Da ist es besser erklärt.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

B
bSharp Themenstarter:in
48 Beiträge seit 2010
vor 13 Jahren

Hallo gfoidl,

rate aus welchem Buch ich das Beispiel habe.

The simplest kind of memory barrier is a full memory barrier (full fence) which prevents any kind of instruction reordering or caching around that fence.

Mir ist absolut klar wie MemoryBarrier das Reordering unterdrückt an eben der Stelle des Aufrufs. Aber die Auswirkung auf Caching wird nicht 100% klar: "prevent ... caching around that fence" ist schwammig.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo bSharp,

hm, also ich finde das relativ klar.

Unoptimiert erfolgt bei while (!complete) jedes mal ein Speicherzugriff auf complete. Optimiert erfolgt dieser nur einmal zu Anfang und ab wird der Wert nur noch aus dem Register gelesen. Durch MemoryBarrier wird nun verhindert, dass ein Speicherzugriff, der vor dem MemoryBarrier erfolgt ist und dessen Ergebnis in einem Register gespeichert wurde, nach dem MemoryBarrier verwendet wird. Also muss complete nun wieder bei jedem Schleifendurchlauf neu aus dem Speicher gelesen werden. Ziel erreicht. Oder siehst du das anders?

herbivore

B
bSharp Themenstarter:in
48 Beiträge seit 2010
vor 13 Jahren

Hallo herbivore,

danke das macht es schon wesentlich klarer.

Könnte man auch einfach sagen, dass ein MemoryBarrier() den Register- und Cache-Inhalt für den jeweils aufrufenden Thread für ungültig erklärt?
Oder wäre das zu unpräzise?

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo bSharp,

ich denke, das wäre zu unpräzise, weil MemoryBarrier ja auch die Umordnung von Instruktionen verhindert. Im Ergebnis ist es eine Mischung aus beidem.

herbivore