Multithreading/Synchronisation auf die Spielkarte



  • Hallo zusammen. Ich habe eine Frage zum allgemeinen Design.

    Aktuell ist der Spielserver an dem ich arbeite nicht wirklich multithreaded.
    Die Spielereingaben kommen im Server an und werden in einer "empfangen" liste gespeichert.
    Der Server arbeitet in einer riesigen Loop diese Spielereingaben ab. Dazu macht er Berechnung der Gegner KI. Ändert die Spielkarte ab und sendet das alles an den Client.

    Nun würden wir aber gerne das ganze Per Multithreading haben da aufwendinge Berechnungen das Spiel zum laggen bringen würden.
    Also ein Thread berechnet die Gegner, Ein Thread ist für die Abarbeitung von N Spielern zuständig.

    Wie wird das am besten auf die Map Synchronisiert, das z.B. dasselbe Feld nicht zweimal belegt wird. Grundidee wäre, für jedes Tile (Feld) ein eigener Mutex. D.H. jede Bewegung würde den Mutex prüfen ob er frei ist und dannach ggf die Bewegung ausführen. Die frage ist dann wie groß ist ein Mutex im Ram da wir bis zu 1000 Karten der Größe 1024x1024 haben können. Und in wie fern würde das Prüfen der mutexes sich auf die allgemeine Geschwindigkeit auswirken?

    Fällt jemand was besseres ein?



  • Fedaykin schrieb:

    Nun würden wir aber gerne das ganze Per Multithreading haben da aufwendinge Berechnungen das Spiel zum laggen bringen würden.

    Wisst ihr das sicher oder ist das eine Vermutung? Wenn es nur eine Vermutung ist: Lass es sein. Die Synchronisation wird höllisch werden, je komplexer es wird.

    Meines Erachtens nach reicht für die Logik ein Thread.
    Von was für Spielerzahlen reden wir denn hier?



  • Die einfachsten Mutexen (Spin-Locks) sind sehr klein, konkret 1 bis 4 Byte.
    Eine CRITICAL_SECTION unter Windows ist etwas grösser, und verwendet zusätzlich noch einen EVENT (Kernel-Objekt, auch nicht ganz klein/billig), der allerdings erst angelegt wird, wenn er das erste mal gebraucht wird (beim 1. "lock" Aufruf der wirklich warten muss, weil ein anderer Thread "in" der CRITICAL_SECTION ist).

    Wie es mit PTHREADS auf den verschidenen *NIXen aussieht kann ich nicht sagen.

    Über die Grösse der Mutexen würde ich mir mal garkeine Gedanken machen, wenn nur 1000 davon gebraucht werden. Selbst wenn eine Mutex 1kb brauchen würde, wäre das in Summe nur 1MB.

    mad_martin hat aber Recht, Synchronisierung ist nicht trivial.



  • hustbaer schrieb:

    Über die Grösse der Mutexen würde ich mir mal garkeine Gedanken machen, wenn nur 1000 davon gebraucht werden. Selbst wenn eine Mutex 1kb brauchen würde, wäre das in Summe nur 1MB.

    Er hatte ja vor, für jedes Feld einen eigenen Mutex bereitzustellen 😉

    So ein lock kostet mehrere tausend Zyklen... entsprechend wird der Geschwindigkeitsvorteil sowieso wahrscheinlich niedriger ausfallen als erwartet.

    Und wenn nicht das ganze Design sorgfältig auf Multithreading ausgelegt ist (es reicht ja nicht, nur Zugriffe auf die Maps zu synchronisieren), fängst du dir eine ganze Reihe von Problemen ein. Von unreproduzierbaren Abstürzen, Sicherheitsproblemen über miserable Performanz wird alles dabei sein.

    Bin ja nicht der erste, der das sagt, aber lass die Logik einfach in einem Thread und konzentriere deine Bemühungen darauf, häufige Aktionen (Spielerbewegungen dürften da die Spitzenreiter sein) so zackig wie möglich abzuarbeiten und allgemein den Overhead von Senden/Empfangen von Nachrichten gering zu halten.

    Edit: moment mal...

    Ändert die Spielkarte ab und sendet das alles an den Client

    Du meinst aber nicht etwa, dass die Spielkarte (bzw. die Gegenstände/Spieler auf ihr) bei jeder Änderung nochmal komplett an alle übertragen wird?



  • Nanyuki schrieb:

    hustbaer schrieb:

    Über die Grösse der Mutexen würde ich mir mal garkeine Gedanken machen, wenn nur 1000 davon gebraucht werden. Selbst wenn eine Mutex 1kb brauchen würde, wäre das in Summe nur 1MB.

    Er hatte ja vor, für jedes Feld einen eigenen Mutex bereitzustellen 😉

    Achherrjeh, ja, das wären dann 1 Mio. Felder pro Karte, also 1 Mio. Locks pro Karte. Das wäre dann doch etwas viel. Mit einfachen Spin-Locks aber dennoch vom Speicher her kein Problem.

    So ein lock kostet mehrere tausend Zyklen... entsprechend wird der Geschwindigkeitsvorteil sowieso wahrscheinlich niedriger ausfallen als erwartet.

    Naja eher so in der Grössenordnung 100 bis 500 Zyklen was ich in Erinnerung habe. Aber auf jeden Fall viel.



  • hustbaer schrieb:

    Naja eher so in der Grössenordnung 100 bis 500 Zyklen was ich in Erinnerung habe. Aber auf jeden Fall viel.

    Stimmt, kann sein, dass sich die Messung, die ich im Sinn hatte, auf etwas anderes bezog (vermutlich im Zusammenhang mit Event-Objekten).

    Übrigens, es dürfte vergleichsweise einfach sein, die Kommunikation in einen oder zwei weitere Threads zu verlagern, also das Empfangen von Rohdaten und Aufbereiten für deinen message handler und umgekehrt. Nach jedem Logiktick kannst du alle angefallenen neuen Nachrichten dem Kommunikationsthread zum Senden übergeben und neue von diesem abholen. Solange du darauf achtest, dass der eine Thread dem anderen nicht eine Verbindung unter dem Hintern wegterminieren kann, kannst du deinen Logikthread so etwas entlasten.


Log in to reply