Muss der Vektor in diesem Fall durch ein Mutex geschützt werden?



  • Hallo zusammen,

    Gegeben ist ein globaler vector<>. Mehrere Threads greifen auf unterschiedliche Indizes dieses vector<>s "gleichzeitig" schreibend zu. Wenn garantiert ist, dass das pro Thread auf unterschiedliche Indizes passiert - müssen dann die Zugriffe durch ein Mutex geschützt werden?

    Danke 🙂



  • Muss ein blankes C-Array geschuetzt werden, wenn nur ueber unterschiedliche Indizes auf die Objekte zugegriffen wird?

    Btw. Selber denken, Glueck verschenken.



  • Na eigentlich nicht, da nie zwei threads eine Schreiboperation auf die gleiche Speicherstelle gleichzeitig durchführen. Aber vielleicht gibt es ja höhere Mechanismen (Speicherverwaltung...), die dem widersprechen !?

    Edit: Oder gibt es die nicht ?



  • Wenn kein Thread auf die Idee kommt, dem Vector ein neues Element hinzuzufügen,
    was eine Reallokation zur Folge haben kann, dann eigentlich nicht.
    Intern verhält sich der Vektor wie ein Array und ein Array ist im Grunde nichts anderes als eine Reihe von unabhängigen Objekten, die eben hintereinander im Speicher liegen. Wenn immer nur ein Thread auf eines dieser Objekte zugreift, passiert da gar nichts.



  • Wenn [...] müssen dann die Zugriffe durch ein Mutex geschützt werden?

    Ich hoffe nicht. Denn dann hätte ich auf der Arbeit Mist gebaut. 😉



  • Dann hab ich wohl auch Glück gehabt 🤡



  • Man könnte auch einen Zeiger auf das erste Element irgendwo ablegen, und die Elemente dann über diesen ansprechen.
    Damit ist vector<> aus dem Spiel - die einzige Anforderung ist dann dass nicht zwei Elemente von unterschiedlichen Threads verwendet werden, und dass keiner eine Reallocation vom vector<> auslöst.

    Ansonsten müsste man mal sehen wie der non-const operator [] definiert ist. Wenn der als Leseoperation definiert ist, dann müsste der Standard auch mit direktem Zugriff garantieren dass es funktioniert.



  • Also bei Java ist es zumindest so, dass wenn mindestens 1 thread auf etwas schreibt und es zusätzlich noch andere leser gibt, synchronisiert werden muss (volatile oder lock).

    Ich weiß nicht genau, ob das bei c++ auch so ist.

    Der Grund ist wohl, dass es beim schreiben sein kann, dass erstmal nur in den cache geschrieben wird oder vielleicht auch erstmal nur in ein register und das geschriebene deswegen für andere unter umständen noch nicht sichtbar ist.

    Korrigiert mich bitte, falls das falsch ist. Aber bei "Java Concurrency in Practie" wurde das so gelehrt, wenn ich das nicht ganz falsch verstanden habe.
    Und dieses Problem sollte ja eigentlich Sprachunabhängig vorhanden sein.


  • Mod

    Q schrieb:

    Also bei Java ist es zumindest so, dass wenn mindestens 1 thread auf etwas schreibt und es zusätzlich noch andere leser gibt, synchronisiert werden muss (volatile oder lock).

    Ich weiß nicht genau, ob das bei c++ auch so ist.

    Das ist richtig, aber hier nicht gefragt.



  • krümelkacker schrieb:

    Wenn [...] müssen dann die Zugriffe durch ein Mutex geschützt werden?

    Ich hoffe nicht. Denn dann hätte ich auf der Arbeit Mist gebaut. 😉

    Dann hast du Mist gebaut. Da je nach Prozessor möglicherweise mehr Bits geschrieben werden, als du eigentlich verändert hast (z.B. änderst du ein Bool, der Prozessor schreibt aber 64 Bit), können benachbarte Werte überschrieben werden. Das hängt vermutlich auch vom Compiler ab, da C++ ja erst im neusten Standard Threads kennt.
    Herb Sutter hatte das mal in einem seiner Thread Artikel für Dr. Dobb's beschrieben (leider kann ich spontan keinen Link liefern). In "Programming with POSIX Threads" von Butenhof werden die Probleme ebenfalls erläutert.



  • manni66, das, was Du meinst, nennt sich "false sharing". Da geht es im Wesentlichen um ein ungeschicktes Speicherlayout und/oder einer ungeschickten Arbeitsaufteilung, die sich Cache-bedingt typischerweise negativ in der Ausführungsgeschwindigkeit auswirkt. Bis auf Unterschiede in der Ausführungsgeschwindigkeit ist der Cache aber transparent und es macht für die Korrektheit keinen Unterschied, wie groß eine Cache-Line ist. Die Geschwindigkeitseinbußen kommen gerade daher, dass die Caches sich wegen false sharing aufwendig synchronisieren müssen (per Cache-Kohärenz-Protokoll). Das wär ja sonst ein ziemlich blödes Speichermodell. Ich bin mir ziemlich sicher, dass im C++2011 Speichermodell der gleichzeitige Zugriff auf benachbarte Bytes (also thread 1 -> byte 1, thread 2 -> byte 2) kein Datenrennen darstellt.

    Da ich in meinem Fall jedem Thread einen kontinuierlichen Bereich des Vektors zuordne, kann es höchstens an den Intervallgrenzen zu false sharing kommen. Und selbst da ist es unwahrscheinlich, weil jeder Thread linear forwärts durch sein eigenes Intervall des Vektors läuft.

    Ich bin jetzt kein multi-CPU-/Cache-Profi, der auf unterster Ebene weiß, was genau passiert. Das muss man aber auch nicht sein, um korrekt programmieren zu können. Da reichen Speichermodelle. Die Caches sich im Fall von false sharing unterhalten, ist mir echt egal.



  • @krümelkacker:
    "False sharing" nennt man das wenn es die Performance drückt, aber alles korrekt funktioniert.
    Wenn es zu unerwünschtem/unerwartetem Verhalten führt, dann nennt man es Scheissendreck. (Ne, sorry, keine Ahnung wie man dann sagt :D)



  • Herb Sutter redet ganz gerne von "false sharing" in diesem Kontext. Ich denke, das ist es, was manni66 im Gedächtnis hatte.



  • krümelkacker schrieb:

    Herb Sutter redet ganz gerne von "false sharing" in diesem Kontext. Ich denke, das ist es, was manni66 im Gedächtnis hatte.

    Nein



  • Sorry, es sieht für mich immer noch so aus, als ob Du das mit dem Chache falsch verstanden hast. Da hat das "Nein" nichts dran geändert, fürchte ich.



  • krümelkacker schrieb:

    Sorry, es sieht für mich immer noch so aus, als ob Du das mit dem Chache falsch verstanden hast. Da hat das "Nein" nichts dran geändert, fürchte ich.

    Naja, mindestens einer von uns liegt falsch 😉



  • BTW: Das Speichermodell von C++0x erlaubt diesbezüglich soweit ich weiss kein unerwartetes Verhalten.



  • hustbaer schrieb:

    BTW: Das Speichermodell von C++0x erlaubt diesbezüglich soweit ich weiss kein unerwartetes Verhalten.

    IMHO muss man dann aber std::atomic<> (oder so ähnlich) verwenden. Ob man dann bei einem Array aber die Elemente auch als atomic definieren muss?



  • manni66 schrieb:

    hustbaer schrieb:

    BTW: Das Speichermodell von C++0x erlaubt diesbezüglich soweit ich weiss kein unerwartetes Verhalten.

    IMHO muss man dann aber std::atomic<> (oder so ähnlich) verwenden. Ob man dann bei einem Array aber die Elemente auch als atomic definieren muss?

    Nein.
    Lass es sein, bitte, und hoer auf krümelkacker.

    atomic ist wieder was ganz anderes.


  • Mod

    1.7/3


Log in to reply