Verwendung von std :: memory_order_consume in der blockierungsfreien SPSC-Warteschlange von Folly

8

Bei dem Versuch, zu verstehen, wie mit sperrfreiem Code umgegangen werden soll, habe ich versucht, eine einzige blockierungsfreie Warteschlange für Verbraucher / Einzelhersteller zu schreiben. Wie immer habe ich Papiere, Artikel und Code überprüft, vor allem wenn man bedenkt, dass dies ein heikles Thema ist.

Ich bin also auf eine Implementierung dieser Datenstruktur in der Folly-Bibliothek gestoßen, die hier zu finden ist: Ссылка

Wie bei jeder blockierungsfreien Warteschlange, die ich gesehen habe, scheint diese einen zirkularen Puffer zu verwenden, also haben wir zwei std::atomic<unsigned int> Variablen: readIndex_ und writeIndex_ . Die readIndex_ geben den nächsten Index an, bei dem wir lesen werden, und writeIndex_ den nächsten, bei dem wir schreiben werden. Scheint einfach genug.

So scheint die Implementierung auf den ersten Blick sauber und ziemlich einfach zu sein, aber ich fand eine Sache problematisch. Einige Funktionen wie isEmpty() , isFull() oder guessSize() verwenden std::memory_order_consume , um den Wert der Indizes abzurufen.

Und um fair zu sein, ich weiß wirklich nicht, welchem ​​Zweck sie dienen. Versteh mich nicht falsch, ich bin mir der Verwendung von std::memory_order_consume im klassischen Fall der Abhängigkeit bewusst, die durch einen Atomzeiger führt, aber hier scheinen wir keine Abhängigkeit zu tragen! Wir haben nur Indizes, vorzeichenlose Ganzzahlen, wir erzeugen keine Abhängigkeiten. Für mich ist in diesem Szenario ein std::memory_order_relaxed gleichwertig.

Ich traue mir jedoch nicht zu, die Speicherordnung besser zu verstehen als diejenigen, die diesen Code entwickelt haben, weshalb ich diese Frage hier stelle. Gibt es etwas, was ich vermisst oder missverstanden habe?

Ich danke Ihnen im Voraus für Ihre Antworten!

    
SideEffects 22.03.2016, 15:57
quelle

1 Antwort

3

Ich dachte dasselbe vor ein paar Monaten, also habe ich diese Pull-Anfrage bereits im Oktober eingereicht und vorgeschlagen Sie ändern die std::memory_order_consume -Lasten in std::memory_order_relaxed , da der Verbrauch einfach keinen Sinn ergibt, da keine Abhängigkeiten vorhanden sind, die von einem Thread zu einem anderen mit diesen Funktionen übertragen werden könnten. Es ergab sich eine Diskussion, die ergab, dass ein möglicher Anwendungsfall für isEmpty() , isFull() und sizeGuess Folgendes war:

%Vor%

Deshalb erklärten sie, dass std::memory_order_relaxed nicht angemessen wäre und std::memory_order_consume wäre. Dies ist jedoch nur der Fall, weil std::memory_order_consume in allen Compilern, die ich kenne, zu std::memory_order_acquire hochgestuft wird. Obwohl std::memory_order_consume scheinbar die richtige Synchronisation liefert, ist es ziemlich irreführend, dies im Code zu belassen und davon auszugehen, dass es korrekt bleibt, insbesondere wenn std::memory_order_consume jemals wie beabsichtigt implementiert werden sollte. Der obige Anwendungsfall würde bei schwächeren Architekturen nicht funktionieren, da die entsprechende Synchronisation nicht erzeugt würde.

Was sie wirklich brauchen, ist diese Ladungen std::memory_order_acquire zu machen, damit dies wie gewünscht funktioniert, weshalb ich dies eingereicht habe andere Pull-Anforderung einige Tage zurück. Alternativ könnten sie die Übernahme Lasten aus der Schleife und verwenden Sie einen Zaun am Ende:

%Vor%

In beiden Fällen wird std::memory_order_consume hier falsch verwendet.

    
Alejandro 09.04.2016, 19:27
quelle