Macht die Hardware-Speicherbarriere die Sichtbarkeit atomarer Operationen schneller und bietet zusätzlich die erforderlichen Garantien?

Lesezeit: 3 Minuten

Benutzer-Avatar
Alex Gutenjew

TL;DR: Macht es jemals Sinn, in einer Producer-Consumer-Warteschlange einen unnötigen (aus Sicht des C++-Speichermodells) Speicherzaun oder eine unnötig starke Speicherreihenfolge zu platzieren, um eine bessere Latenzzeit auf Kosten eines möglicherweise schlechteren Durchsatzes zu erzielen?


Das C++-Speichermodell wird auf der Hardware ausgeführt, indem eine Art Speicherzäune für stärkere Speicherordnungen vorhanden sind und sie nicht für schwächere Speicherordnungen vorhanden sind.

Insbesondere, wenn der Produzent dies tut store(memory_order_release)und Verbraucher beobachtet den gespeicherten Wert mit load(memory_order_acquire), gibt es keine Zäune zwischen Ladung und Lager. Auf x86 gibt es überhaupt keine Zäune, auf ARM werden Zäune vor dem Speichern und nach dem Laden ausgeführt.

Der ohne Zaun gespeicherte Wert wird eventuell durch Laden ohne Zaun eingehalten (evtl. nach einigen erfolglosen Versuchen)

Ich frage mich, ob das Anbringen eines Zauns auf beiden Seiten der Warteschlange den zu beobachtenden Wert beschleunigen kann? Wie hoch ist die Latenz mit und ohne Zaun, wenn ja?

Ich erwarte, dass nur eine Schleife mit load(memory_order_acquire) und pause / yield Die Beschränkung auf Tausende von Iterationen ist die beste Option, da sie überall verwendet wird, aber Sie möchten verstehen, warum.

Da es bei dieser Frage um das Hardwareverhalten geht, gehe ich davon aus, dass es keine allgemeine Antwort gibt. Wenn ja, wundere ich mich hauptsächlich über x86 (x64-Geschmack) und in zweiter Linie über ARM.


Beispiel:

T queue[MAX_SIZE]

std::atomic<std::size_t>   shared_producer_index;

void producer()
{
   std::size_t private_producer_index = 0;

   for(;;)
   {
       private_producer_index++;  // Handling rollover and queue full omitted

       /* fill data */;

      shared_producer_index.store(
          private_producer_index, std::memory_order_release);
      // Maybe barrier here or stronger order above?
   }
}


void consumer()
{
   std::size_t private_consumer_index = 0;

   for(;;)
   {
       std::size_t observed_producer_index = shared_producer_index.load(
          std::memory_order_acquire);

       while (private_consumer_index == observed_producer_index)
       {
           // Maybe barrier here or stronger order below?
          _mm_pause();
          observed_producer_index= shared_producer_index.load(
             std::memory_order_acquire);
          // Switching from busy wait to kernel wait after some iterations omitted
       }

       /* consume as much data as index difference specifies */;

       private_consumer_index = observed_producer_index;
   }
}

  • Die Bereitstellung eines Codebeispiels wäre hier hilfreich. Ich bin mir nicht ganz sicher, was Sie fragen

    – Bartop

    4. Mai 2020 um 11:41 Uhr

  • Ich habe ein Beispiel gegeben, obwohl die Frage beantwortet ist

    – Alex Gutenjew

    4. Mai 2020 um 12:54 Uhr

  • @bartop: Nur meine 2 Cent: Es schien mir ohne Beispiel klar zu sein. Es könnte einer dieser Fälle sein, in denen es für Leute, die die Antwort kennen, aus der Frage klar ist. Es ist wahrscheinlich keine schlechte Sache, eine zu haben, vielleicht hilft es mehr Lesern, den Sinn meiner Antwort zu verstehen. (Es geht um den Versuch, die Latenz zwischen den Kernen zu minimieren.)

    – Peter Cordes

    4. Mai 2020 um 13:19 Uhr

  • Ich sehe, dass die Frage ein Duplikat davon ist, aber ich habe sie in C++-Begriffen gestellt, nicht in Hardware-Begriffen, daher konnte ich keine anderen Fragen finden. Die Latenz war meine Sorge, da ich wusste, dass die CPU bereits versucht, diese Speicher bald zu liefern, und sie in höchstens 1 Mikrosekunde da sein wird, reicht mir aus.

    – Alex Gutenjew

    4. Mai 2020 um 12:30 Uhr

  • @AlexGuteniev: Wenn ich dachte, es wäre ein echtes Duplikat, hätte ich es einfach geschlossen, anstatt zu antworten. Nicht-triviale Themen wie diese scheinen es wert zu sein, beantwortet zu werden, auch wenn es fast ein Duplikat ist, im Gegensatz zu Hausaufgabenfragen für Anfänger. Und diese hier begann nicht mit einer Reihe von Missverständnissen, die korrigiert werden mussten, daher schien es ein guter Ort zu sein, um mein aktuelles Verständnis als kanonische Antwort zu besprechen, auf die später sinnvollerweise verwiesen werden könnte.

    – Peter Cordes

    4. Mai 2020 um 12:36 Uhr

  • Siehe auch: Ist eine Speicherbarriere erforderlich, um einen Wert zu lesen, der atomar modifiziert ist? – Diese Frage sorgte sich auch um die Korrektheit, wie die Sache mit dem “neuesten Wert”, als ob die Zeit über Threads hinweg so funktionierte.

    – Peter Cordes

    5. April um 3:35


1012910cookie-checkMacht die Hardware-Speicherbarriere die Sichtbarkeit atomarer Operationen schneller und bietet zusätzlich die erforderlichen Garantien?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy