Frage

Plain Load hat eine erfasste Semantik auf x86, Plain Store hat eine Release-Semantik, der Compiler kann jedoch weiterhin Anweisungen neu anordnen.Während Zäune und gesperrte Anweisungen (gesperrtes xchg, gesperrtes cmpxchg) sowohl die Hardware als auch den Compiler daran hindern, neu zu ordnen, sind einfache Ladevorgänge und Speicher weiterhin erforderlich, um sie mit Compiler-Barrieren zu schützen.Visual C ++ bietet die Funktion _ReadWriterBarrier () , die eine Neuordnung des Compilers verhindert, und C ++ bietet aus demselben Grund auch ein flüchtiges Schlüsselwort.Ich schreibe all diese Informationen, nur um sicherzustellen, dass ich alles richtig mache.Also alles oben geschriebene ist wahr, gibt es einen Grund, als flüchtige Variablen zu markieren, die in Funktionen verwendet werden, die mit _ReadWriteBarrier () geschützt sind?

Beispielsweise:

int load(int& var)
{
    _ReadWriteBarrier();
    T value = var;
    _ReadWriteBarrier();
    return value;
}

Ist es sicher, diese Variable nichtflüchtig zu machen?Soweit ich weiß, ist es so, weil die Funktion geschützt ist und vom Compiler im Inneren keine Neuordnung vorgenommen werden kann.Auf der anderen Seite bietet Visual C ++ ein spezielles Verhalten für flüchtige Variablen (anders als das standardmäßige), es führt flüchtige Lese- und Schreibvorgänge atomarer Ladungen und Speicher durch, aber mein Ziel ist x86 und einfache Ladungen und Speicher sollen auf x86 atomar sein sowieso, richtig?

Danke im Voraus.

War es hilfreich?

Lösung

volatiles-Schlüsselwort ist auch in c verfügbar. "volatil" wird häufig in eingebettetem System verwendet, insbesondere wenn der Wert der Variablen zu jeder Zeit ändern kann - ohne dass irgendwelche Maßnahmen der Code ergriffen werden - drei gängige Szenarien umfassen das Lesen eines speicherkabalisierten Peripherieregisters oder globalen Variablen, die entweder von einem modifiziert werden Interrupt-Service-Routine oder in einem Multi-Thread-Programm.

So ist es das letzte Szenario, in dem volatil als _ReadwriteBarrier ähnlich ist.

_ReadwriteBarrier ist keine Funktion - _ReadWriteBarrier legt keine zusätzlichen Anweisungen ein, und es verhindern nicht, dass die CPU vor dem Neuvorgang neu gesendet und schreibt. Es verhindert nur, dass der Compiler sie neu ordnet. _ReadwriteBarrier besteht darin, die Neuordnung von Compiler zu verhindern.

modeBarrier ist die Verhinderung der CPU-Neuordnung!

Ein Compiler ordnet in der Regel Anweisungen an. Mit MSVC verwenden Sie _ReadwriteBarrier im Code, so dass der Compiler nicht liest und schreibt.

Überprüfen Sie diesen Link für detailliertere Diskussionen zu diesen Themen http://msdn.microsoft.com/de- US / Bibliothek / EE418650 (v= Vs.85) .aspx

In Bezug auf Ihren Code-Snippet - Sie müssen LeseWriteBarrier nicht als Sync-Primitive verwenden - der erste Anruf an _ReadwriteBarrier ist nicht erforderlich.

Bei Verwendung von ReadWriteBarrier müssen Sie nicht volatile verwenden

Sie haben geschrieben "Es macht volatiler Ladungen und schreibt atomare Lasten und Filialen" - ich glaube nicht, dass das in Ordnung ist, dass die Zerstäuber und Volatilität anders ist. Atomoperationen gelten als unteilbar - ... http://www.yoda. Arachsys.com/csharp/threads/volatility.shtml

Andere Tipps

Beachten:Ich bin kein Experte zu diesem Thema, einige meiner Aussagen sind "was ich im Internet gehört habe", aber ich glaube, ich kann noch einige Missverständnisse aufklären.

[bearbeiten] Im Allgemeinen würde ich mich auf plattformspezifische Eigenschaften wie atomare x86-Lesevorgänge und das Fehlen von OOOX nur bei isolierten, lokalen Optimierungen verlassen, die von einem #ifdef überprüfung der Zielplattform, idealerweise begleitet von einer portablen Lösung im #else Pfad.

Dinge, auf die Sie achten sollten

  • atomarität von Lese- / Schreiboperationen
  • neuordnung aufgrund von Compiler-Optimierungen (dies schließt eine andere Reihenfolge ein, die von einem anderen Thread aufgrund des einfachen Register-Caching gesehen wird)
  • ausführung außerhalb der Reihenfolge in der CPU

Mögliche Missverständnisse

1. Soweit ich weiß, ist es so, weil die Funktion geschützt ist und vom Compiler im Inneren keine Neuordnung vorgenommen werden kann.
[bearbeiten] Klären:der _ReadWriteBarrier bietet Schutz vor Befehlsumordnung, Sie müssen jedoch über den Rahmen der Funktion hinausschauen. _ReadWriteBarrier wurde in VS 2010 behoben, um dies zu tun, frühere Versionen können fehlerhaft sein (abhängig von den Optimierungen, die sie tatsächlich durchführen).

Optimierung ist nicht auf Funktionen beschränkt.Es gibt mehrere Mechanismen (automatisches Inlining, Generierung von Link-Timecode), die Funktionen und sogar Kompilierungseinheiten umfassen (und weitaus bedeutendere Optimierungen bieten können als Register-Caching mit kleinem Bereich).

2. Visuelles C ++ [...] macht flüchtige Lese- und Schreibvorgänge atomare Ladungen und Speicher,
Wo hast du das gefunden? MSDN sagt, dass über den Standard hinaus Speicherbarrieren um Lese- und Schreibvorgänge gelegt werden, keine Garantie für atomare Lesevorgänge.

[bearbeiten] Beachten Sie, dass C #, Java, Delphi usw.haben unterschiedliche Speicher-Mdoels und können unterschiedliche Garantien geben.

3. einfache Ladungen und Speicher sollen auf x86 sowieso atomar sein, oder?
Nein, sind sie nicht.Nicht ausgerichtete Lesevorgänge sind nicht atomar.Sie zufällig sein atomar, wenn sie gut ausgerichtet sind - eine Tatsache, auf die ich mich nicht verlassen würde, es sei denn, sie ist isoliert und leicht austauschbar.Andernfalls wird Ihre "Vereinfachung für x86" zu einer Sperre für dieses Ziel.

[bearbeiten] Nicht ausgerichtete Lesevorgänge passieren:

char * c = new char[sizeof(int)+1];
load(*(int *)c);      // allowed by standard to be unaligned
load(*(int *)(c+1));  // unaligned with most allocators

#pragma pack(push,1)
struct 
{
   char c;
   int  i;
} foo;
load(foo.i);         // caller said so
#pragma pack(pop)

Dies ist natürlich alles akademisch, wenn Sie sich daran erinnern, dass der Parameter ausgerichtet sein muss und Sie den gesamten Code steuern.Ich würde keinen solchen Code mehr schreiben, weil ich zu oft von Faulheit der Vergangenheit gebissen wurde.

4. Einfaches Laden hat eine Erwerbssemantik auf x86, einfaches Speichern hat eine Release-Semantik
Nein.x86-Prozessoren verwenden keine Ausführung außerhalb der Reihenfolge (oder besser gesagt, kein sichtbares OOOX - glaube ich), aber dies hindert den Optimierer nicht daran, Anweisungen neu zu ordnen.

5. _ReadBarrier / _WriteBarrier / _ReadWriteBarrier mach die ganze Magie Sie tun es nicht - sie verhindern nur eine Neuordnung durch den Optimierer.MSDN hat es endlich geschafft große böse Warnung für VS2010, aber die Informationen gelten anscheinend für auch frühere Versionen.


Nun zu deiner Frage.

Ich gehe davon aus, dass der Zweck des Snippets darin besteht, eine beliebige Variable N zu übergeben und sie zu laden (atomar?) Die einfache Wahl wäre ein verriegelter Lesevorgang oder (unter Visual C ++ 2005 und höher) ein flüchtiger Lesevorgang.

Andernfalls würden Sie vor dem Lesen eine Barriere für Compiler und CPU benötigen, im VC ++ - Salon wäre dies:

int load(int& var)
{   
  // force Optimizer to complete all memory writes:
  // (Note that this had issues before VC++ 2010)
   _WriteBarrier();    

  // force CPU to settle all pending read/writes, and not to start new ones:
   MemoryBarrier();

   // now, read.
   int value = var;    
   return value;
}

Nein das _WriteBarrier hat eine zweite Warnung in MSDN:* In früheren Versionen des Visual C ++-Compilers wurden die Funktionen _ReadWriteBarrier und _WriteBarrier nur lokal erzwungen und wirkten sich nicht auf Funktionen in der Aufrufstruktur aus.Diese Funktionen werden jetzt im Aufrufbaum ganz nach oben erzwungen.*


I hoffen das ist richtig.stackoverflowers, bitte korrigiere mich, wenn ich falsch liege.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top