Mutex Implementierung



  • Achja...

    camper schrieb:

    Kellerautomat schrieb:

    In Schleifen immer compare_exchange_weak verwenden.

    Sehe ich an dieser Stelle nicht als wesentlich, da der Tread in jedem Fall nur Fortschritte machen kann, wenn das CAS erfolgreich ist.

    compare_exchange_weak ist (vermute ich mal) oft so implementiert dass erstmal ein load(relaxed) gemacht wird, und wenn das nicht dem erwarteten Wert entspricht dann wird gar nicht erst mit CAS draufgekloppt.
    Und das ist besser.

    Ich vermute deswegen, weil mehrere Threads die gleichzeitig um die selbe Mutex kämpfen dann alle load(relaxed) machen, was dazu führt dass die beteiligten Cores die Cache-Row mit der Spin-Variable alle "shared" im Cache haben.
    So lange die Mutex nicht freigegeben wird, wird die Cache-Row also aus keinem der Caches evakuiert.
    Der shared L3 Cache/Front-Side-Bus bzw. wo auch immer dann alles zusammenläuft wird dabei viel weniger gestresst als wenn man dauernd mit CAS (von unterschidlichen Cores aus) auf die Cache-Row draufhaut.
    Und das ist gut für Cores die mit der Mutex gar nix zu tun haben, aber auch gern hin und wieder nen Speicherzugriff machen würden.

    Heisst: ich denke nicht dass es egal ist, sondern im Gegenteil dass das gerade der Paradefall für wo man compare_exchange_weak statt compare_exchange_strong verwenden sollte.

    Zumindest macht man genau das wenn man es in Assembler implementiert. Erstmal nen Loop der mit nem normalen Load (atomic, relaxed) so lange spult bis man den erwarteten Wert gelesen hat, und dann mit CAS draufhauen. Wenns nicht geklappt hat wieder mit normalem Load loopen usw.


  • Mod

    hustbaer schrieb:

    compare_exchange_weak ist (vermute ich mal) oft so implementiert dass erstmal ein load(relaxed) gemacht wird, und wenn das nicht dem erwarteten Wert entspricht dann wird gar nicht erst mit CAS draufgekloppt.

    Das würde nicht funktionieren, weil der relaxed load eben nicht sicher erkennen würde, ob der lock frei ist (fehlende Synchronisation mit unlock). Die weak-Variante hat ihren Grund in Rechnerarchitekturen, die CAS durch LL/SC (load-link/store-conditional) implementieren.



  • Also ich hab' mal die Implementierung von MSVC gecheckt, und die verwendet seltsamerweise wirklich sofort nen InterlockedCompareExchange .

    Funktionieren tut das aber super, wieso sollte es auch nicht? Dass es theoretisch dazu kommen könnte dass der lockende Thread den von unlock gesetzten Wert nie liest ist uninteressant, in der Praxis kommt das nicht vor. IIRC schreibt sogar der Standard dass Änderungen auch ohne Synchronisierung "bald mal" für alle anderen CPUs/Cores sichtbar werden sollten. Und danach kommt dann ja eh der normale CAS, der dann die entsprechenden Memory-Order-Garantien umsetzt.


  • Mod

    hustbaer schrieb:

    Also ich hab' mal die Implementierung von MSVC gecheckt, und die verwendet seltsamerweise wirklich sofort nen InterlockedCompareExchange .

    Auf x86 ist das sowieso uninteressant. Da bei dieser Architektur Schreib-/und Lesevorgänge so gut wie nie sichtbar umgeordnet werden, existieren "relaxed" loads dort praktisch nicht.

    hustbaer schrieb:

    Dass es theoretisch dazu kommen könnte dass der lockende Thread den von unlock gesetzten Wert nie liest ist uninteressant, in der Praxis kommt das nicht vor. IIRC schreibt sogar der Standard dass Änderungen auch ohne Synchronisierung "bald mal" für alle anderen CPUs/Cores sichtbar werden sollten.

    Und dies irgendwann mal hälst du für ausreichend? Das sieht dann im nächsten Benchmark sicher nicht gut aus.

    hustbaer schrieb:

    Und danach kommt dann ja eh der normale CAS, der dann die entsprechenden Memory-Order-Garantien umsetzt.

    Wenn er kommt, was ja nicht der Fall ist, falls dein relaxed load einen besetzten Mutex anzeigt, obwohl er frei ist.



  • Gibt es echt Plattformen auf denen das länger dauert?
    Das wäre dann natürlich doof.

    camper schrieb:

    hustbaer schrieb:

    Also ich hab' mal die Implementierung von MSVC gecheckt, und die verwendet seltsamerweise wirklich sofort nen InterlockedCompareExchange .

    Auf x86 ist das sowieso uninteressant. Da bei dieser Architektur Schreib-/und Lesevorgänge so gut wie nie sichtbar umgeordnet werden, existieren "relaxed" loads dort praktisch nicht.

    Gerade auf x86 wäre es deswegen gut zum Locken einer Mutex erstmal einen relaxed Load zu machen.


Anmelden zum Antworten