Wie reduziert die Delta-Komprimierung die Datenmenge, die über das Netzwerk gesendet wird?

26

Viele Spiele verwenden die Technik der Delta-Komprimierung, um die gesendete Datenlast zu verringern. Ich verstehe nicht, wie diese Technik tatsächlich die Datenlast senkt?

Angenommen, ich möchte eine Position senden. Ohne Delta-Komprimierung sende ich zum Beispiel eine vector3mit der genauen Position der Entität (30, 2, 19). Mit Delta-Komprimierung sende ich eine vector3mit kleineren Zahlen (0.2, 0.1, 0.04).

Ich verstehe nicht, wie es die Datenlast senkt, wenn beide Nachrichten vector3- 32 Bit für jedes Float - 32 * 3 = 96 Bit sind!

Ich weiß, dass Sie jedes Float in ein Byte konvertieren und es dann wieder von einem Byte in ein Float konvertieren können, aber es verursacht sichtbare Präzisionsfehler.

user101051
quelle
Jedes Mal, wenn Sie sich mit einem Spiel vernetzen, das Delta-Komprimierung in irgendeiner Form verwendet, müssen Sie perfekt deterministisch sein (Fehler und Desynchronisierung). Ob "Konvertieren von Byte in Float" (oder was auch immer) zu Präzisionsfehlern führt, ist nicht wichtig - die notwendige Bedingung ist, dass in allen synchronisierten Maschinen / Spielen alles genau gleich ist. Wenn es "Präzisionsfehler" gibt (unvermeidbar, auch wenn Sie volle Floats verwendet haben - Ihre CPU verwendet nicht die gleichen Floats wie meine CPU), müssen sie auf allen Computern gleich sein - und sind daher nicht sichtbar. Sie haben die Typen ausgewählt, um sichtbare Effekte zu vermeiden.
Luaan
2
@Luaan oder Sie können von Zeit zu Zeit einen teilweisen absoluten Status hinzufügen. Beispielsweise können Sie von Zeit zu Zeit einige Objekte auswählen und die absolute Position übergeben. Wählen Sie lieber Objekte aus, die sich in der Nähe des Spielers befinden.
Ratschenfreak
Irgendwie hatte ich erwartet, dass es sich bei dieser Frage um einen Verwandten von rsync handelt ...
SamB
Huffman-Codierung.
Ben Voigt
Verwenden Sie einfach Middle-Out-Mann
John Demetriou

Antworten:

41

Es gibt Situationen, in denen Sie das Senden des vollständigen Spielstatus nicht vermeiden können, z. B. beim Laden eines gespeicherten Multiplayer-Spiels oder wenn eine erneute Synchronisierung erforderlich ist. Aber vollen Zustand zu senden ist in der Regel vermeidbar, und das ist , wo Delta - Codierung kommt in der Regel ist dies. Alle , dass Delta - Kompression ist über; Ihr Beispiel beschreibt diese Situation nicht wirklich. Der Grund, warum Delta-Komprimierung sogar erwähnt wird, ist, dass naive Implementierungen oft eher Status als Deltas senden, da Status normalerweise das ist, was eine naive Spiel-Implementierung sowieso speichert. Deltas sind dann eine Optimierung.

Mit Deltas würden Sie niemals die Positionen von Einheiten senden, die sich überhaupt nicht bewegt haben. Das ist der Geist davon.

Stellen Sie sich vor, wir wären jahrelang Brieffreunde und ich hätte mein Gedächtnis verloren (und alle Ihre Briefe verworfen, nachdem ich sie gelesen hatte). Anstatt einfach wie gewohnt mit Ihrer Serie von Briefen fortzufahren, müssten Sie mir die gesamte Geschichte Ihres Lebens in einem massiven Brief schreiben, damit ich Sie einholen kann.

In Ihrem gegebenen Fall kann es (abhängig von Ihrer Codebasis) möglich sein, eine geringere Anzahl von Bits zum Codieren der Deltas zu verwenden, im Gegensatz zu den großen Bitbereichen, die zum Senden des vollständigen Zustands benötigt werden. Angenommen, die Welt hat einen Durchmesser von vielen Kilometern. Möglicherweise benötigen Sie einen 32-Bit-Float, um Positionen bis auf einen Zentimeter in einer bestimmten Achse genau zu codieren. Angesichts der für Objekte geltenden Maximalgeschwindigkeit, die nur einige Meter pro Tick betragen kann, kann dies jedoch in nur 8 Bit erfolgen (2 ^ 8 = 256, also ausreichend, um maximal 200 cm zu speichern). Dies setzt natürlich eher eine feste als eine Gleitkommazahl voraus ... oder eine Art Halb- / Viertel-Gleitkommazahl wie in OpenGL, wenn Sie keine Probleme mit festen Punkten haben möchten.

Ingenieur
quelle
7
Ihre Antwort ist mir nicht klar. Ich habe das Gefühl, die Information eines sich nicht bewegenden Objekts nicht zu senden, ist nur ein Nebeneffekt der Delta-Codierung und nicht "The spirit of it". Der wahre Grund für die Verwendung der Delta-Codierung scheint in der Antwort von Ratschenfreak besser hervorgehoben zu sein.
Etsitpab Nioliv
14
@EtsitpabNioliv "The Spirit" ist einfach "nicht senden, was Sie nicht müssen". Dies kann auf die Bit-Ebene reduziert werden - "Verwenden Sie nur so viel Bandbreite, wie Sie benötigen, um die erforderliche Nachricht über die Leitung zu erhalten". Diese Antwort scheint offensichtlich für alle anderen klar genug zu sein. Vielen Dank.
Ingenieur
4
@EtsitpabNioliv Schon mal erfahren, wie SVN Dateien serverseitig speichert? Es wird nicht bei jedem Commit die gesamte Datei gespeichert. Es speichert Deltas und die Änderungen, die jedes Commit enthält. Der Begriff "Delta" wird häufig in Mathematik und Programmierung verwendet, um die Differenz zwischen zwei Werten zu bezeichnen. Ich bin kein Spielprogrammierer, aber ich würde mich wundern, wenn die Verwendung beim Spielen so viel anders ist. Die Idee macht dann Sinn: Sie "komprimieren" die Menge der Daten, die Sie senden müssen, indem Sie nur die Unterschiede statt alles senden. (Wenn ein Teil davon für mich verwirrend ist, ist es das Wort "komprimieren".)
jpmc26
1
Außerdem haben kleine Zahlen eine höhere Anzahl von Null-Bits, und die Verwendung des geeigneten Komprimierungsalgorithmus zum Codieren / Decodieren von gesendeten Informationen kann dazu führen, dass noch kleinere Nutzdaten über das Netzwerk gesendet werden.
Liggiorgio
22

Sie haben das falsche Delta. Sie betrachten das Delta der einzelnen Elemente. Sie müssen an das Delta der gesamten Szene denken.

Angenommen, Ihre Szene enthält 100 Elemente, von denen jedoch nur eines verschoben wurde. Wenn Sie 100 Elementvektoren senden, werden 99 davon verschwendet. Sie müssen wirklich nur 1 senden.

Angenommen, Sie haben ein JSON-Objekt, in dem alle Ihre Elementvektoren gespeichert sind. Dieses Objekt wird zwischen Ihrem Server und Ihrem Client synchronisiert. Anstatt zu entscheiden, "hat sich so und so bewegt?" Sie können einfach Ihren nächsten Spiel-Tick in einem JSON-Objekt generieren, ein erstellen diff tick100.json tick101.jsonund diesen Diff senden. Auf der Clientseite wenden Sie das Diff auf den Vektor Ihres aktuellen Ticks an, und schon sind Sie fertig.

Dabei nutzen Sie die jahrzehntelange Erfahrung bei der Erkennung von Unterschieden im Text (oder in der Binärdarstellung!) Und müssen sich nicht darum kümmern, selbst etwas zu verpassen. Idealerweise verwenden Sie jetzt auch eine Bibliothek, die dies hinter den Kulissen tut, um es Ihnen als Entwickler noch einfacher zu machen.

corsiKa
quelle
2
Klingt diffnach einem ineffizienten Hack. Es ist nicht erforderlich, den JSON-String auf der empfangenden Seite zu belassen, ihn jedes Mal zu patchen und zu deserialisieren. Das Berechnen der Differenz zweier Schlüsselwertwörterbücher ist keine komplizierte Aufgabe. Sie durchlaufen einfach alle Schlüssel und überprüfen, ob die Werte gleich sind. Wenn nicht, fügen Sie sie dem resultierenden Schlüsselwert-Dikt hinzu und senden das Dikt anschließend serialisiert an JSON. Einfach, keine jahrelange Erfahrung erforderlich. Im Gegensatz zu Diffs umfasst diese Methode: 1) keine alten (ersetzten) Daten; 2) spielt besser mit UDP; 3) nicht auf newlines angewiesen
gronostaj
8
@gronostaj Dies war ein Beispiel, um den Punkt zu vermitteln. Ich befürworte eigentlich nicht die Verwendung von diff für JSON - deshalb sagen Sie "Angenommen".
corsiKa
2
"Auf diese Weise nutzen Sie die jahrzehntelange Erfahrung bei der Erkennung von Unterschieden im Text (oder in der Binärdarstellung!) Und müssen sich keine Sorgen mehr machen, wenn Sie selbst etwas verpassen. Idealerweise verwenden Sie jetzt auch eine Bibliothek, die dies hinter den Kulissen erledigt, damit Sie es sogar schaffen Entlastet Sie als Entwickler. " In diesem Teil klingt es definitiv so, als würden Sie vorschlagen, ein Diff oder eine Bibliothek zu verwenden, die ein Diff verwendet, wenn dies vernünftigerweise niemand tun würde. Ich würde Delta-Komprimierung nicht als "unterschiedlich" bezeichnen, es ist nur Delta-Komprimierung, die Ähnlichkeiten sind oberflächlich
Selali Adobor
Ein optimales Diff und eine optimale Delta-Komprimierung sind gleich. Während das Diff-Dienstprogramm in der Befehlszeile auf Textdateien ausgerichtet ist und wahrscheinlich kein optimales Ergebnis liefert, würde ich empfehlen, Bibliotheken zu durchsuchen, die die Delta-Komprimierung für Sie durchführen. Das Wort Delta ist nichts Besonderes - Delta und Diff bedeuten in diesem Sinne buchstäblich dasselbe. Das scheint im Laufe der Jahre verloren gegangen zu sein.
corsiKa
9

Sehr oft wird ein anderer Komprimierungsmechanismus in Kombination mit einer Delta-Codierung verwendet, wie zum Beispiel eine arithmetische Komprimierung.

Diese Komprimierungsschemata funktionieren viel besser, wenn die möglichen Werte vorhersehbar gruppiert sind. Bei der Delta-Codierung werden die Werte um 0 gruppiert.

Ratschenfreak
quelle
1
Ein weiteres Beispiel: Wenn Sie 100 Raumschiffe mit jeweils eigener Position haben, aber mit demselben Geschwindigkeitsvektor unterwegs sind, müssen Sie die Geschwindigkeit nur einmal senden (oder zumindest richtig komprimieren lassen ). Andernfalls müssten Sie stattdessen 100 Positionen senden. Lassen Sie andere das Hinzufügen tun. Wenn Sie Shared-State-Lock-Step-Simulationen als aggressive Form der Delta-Komprimierung betrachten, senden Sie nicht einmal die Geschwindigkeiten, sondern nur die Befehle, die vom Player kommen. Lassen Sie noch einmal jeden selbst hinzufügen.
Luaan
Genau. Komprimierung ist in der Antwort relevant.
Leopoldo Sanczyk
8

Sie haben im Großen und Ganzen recht, aber einen wichtigen Punkt übersehen.

Entitäten in einem Spiel werden durch viele Attribute beschrieben, von denen die Position nur eine ist .

Welche Attribute? In einem vernetzten Spiel kann dies, ohne dass Sie zu viel nachdenken müssen, Folgendes umfassen:

  • Position.
  • Orientierung.
  • Aktuelle Bildnummer.
  • Farbe / Lichtinfo.
  • Transparenz.
  • Modell zu verwenden.
  • Textur zu verwenden.
  • Spezialeffekte.
  • Etc.

Wenn Sie diese einzeln auswählen, können Sie sicher den Fall herbeiführen, dass sie in einem bestimmten Frame, der geändert werden muss, vollständig erneut übertragen werden muss.

Es ändern sich jedoch nicht alle diese Attribute mit der gleichen Geschwindigkeit .

Modell ändert sich nicht? Ohne Delta-Komprimierung muss es trotzdem erneut übertragen werden. Mit Delta-Kompression muss es nicht sein.

Position und Orientierung sind zwei Fälle, die interessanter sind und üblicherweise aus jeweils 3 Schwimmern bestehen. Zwischen zwei gegebenen Frames besteht die Möglichkeit, dass sich nur 1 oder 2 von jedem Satz von 3 Floats ändern können. In einer geraden Linie bewegen? Nicht drehen? Kein Springen? Dies sind alle Fälle, in denen Sie ohne Delta-Komprimierung eine vollständige Neuübertragung durchführen müssen, bei Delta-Komprimierung jedoch nur die Änderungen erneut übertragen müssen.

Maximus Minimus
quelle
8

Sie haben Recht, dass eine naive Delta-Berechnung für sich genommen, bei der das Ergebnis in der gleichen Größenstruktur wie die Operanden gespeichert und ohne weitere Verarbeitung übertragen wird, keinen Datenverkehr spart.

Es gibt jedoch zwei Möglichkeiten, wie ein gut konzipiertes System, das auf Deltas basiert, Datenverkehr sparen kann.

Erstens ist das Delta in vielen Fällen Null. Sie können Ihr Protokoll so gestalten, dass Sie es gar nicht senden, wenn das Delta Null ist. Dies ist natürlich mit einem gewissen Aufwand verbunden, da Sie angeben müssen, was Sie senden oder nicht, aber insgesamt ist es wahrscheinlich ein großer Gewinn.

Zweitens haben die Deltas normalerweise einen viel kleineren Wertebereich als die ursprünglichen Zahlen. und dieser Bereich wird auf Null zentriert. Dies kann ausgenutzt werden, indem für die meisten Deltas ein kleinerer Datentyp verwendet wird oder indem der gesamte Datenstrom durch einen Allzweck-Komprimierungsalgorithmus geleitet wird.

Peter Green
quelle
6

Während in den meisten Antworten davon die Rede ist, dass bei der Delta-Codierung nur die Änderungen an den Gesamtstatus gesendet werden, gibt es eine andere Funktion, die als Filter verwendet werden kann, um die Datenmenge zu reduzieren, die im Vollstatus komprimiert werden muss Update auch, was sein kann, woher die Verwirrung in der Frage kommt, wie gestellt.

Bei der Codierung eines Zahlenvektors können Sie in einigen Fällen (z. B. Ganzzahlen, Aufzählungen usw.) eine Codierung mit variablen Bytes für die einzelnen Elemente verwenden. In einigen Fällen können Sie die für jedes Element erforderliche Datenmenge weiter reduzieren Wenn Sie es entweder als laufende Summe oder als Mindestwert und Differenz zwischen jedem Wert und diesem Mindestwert speichern.

Wenn Sie beispielsweise den Vektor codieren möchten, können {68923, 69012, 69013, 69015}Sie diesen als Delta-Codierung ausführen {68923, 89, 1, 2}. Bei Verwendung einer einfachen Codierung mit variablen Bytes, bei der Sie 7 Datenbits pro Byte speichern und mit einem Bit angeben, dass ein weiteres Byte ansteht, würde jedes einzelne Element im Array 3 Bytes für die Übertragung benötigen, jedoch die Delta-codierte Version würde nur 3 Bytes für das erste Element und 1 Byte für die verbleibenden Elemente erfordern; Abhängig von der Art der Daten, die Sie serialisieren, kann dies zu beeindruckenden Einsparungen führen.

Dies ist jedoch eher eine Serialisierungsoptimierung und nicht das, was allgemein gemeint ist, wenn wir über "Delta-Codierung" sprechen, wenn es darum geht, beliebige Daten als Teil des Spielzustands (oder Video oder dergleichen) zu streamen. Andere Antworten können dies bereits hinreichend erklären.

flauschige
quelle
4

Es ist auch erwähnenswert, dass Kompressionsalgorithmen auf dem Diff besser funktionieren. Wie in anderen Antworten erwähnt, bleiben die meisten Ihrer Elemente zwischen zwei Zuständen gleich, oder die Werte ändern sich um einen kleinen Bruchteil. In beiden Fällen führt die Anwendung eines Komprimierungsalgorithmus auf die Differenz Ihres Zahlenvektors zu erheblichen Einsparungen. Auch wenn Sie nicht gelten keine zusätzliche Logik zum Vektor wie die 0 Elemente zu entfernen.

Hier ist ein Beispiel in Python:

import numpy as np
import zlib
import json
import array


state1 = np.random.random(int(1e6))

diff12 = np.r_[np.random.random(int(0.1e6)), np.zeros(int(0.9e6))]
np.random.shuffle(diff12) # shuffle so we don't cheat by having all 0s one after another
state2 = state1 + diff12

state3 = state2 + np.random.random(int(1e6)) * 1e-6
diff23 = state3 - state2

def compressed_size(nrs):
    serialized = zlib.compress(array.array("d", nrs).tostring())
    return len(serialized)/(1024**2)


print("Only 10% of elements change for state2")
print("Compressed size of diff12: {:.2f}MB".format(compressed_size(diff12)))
print("Compressed size of state2: {:.2f}MB".format(compressed_size(state2)))

print("All elements change by a small value for state3")
print("Compressed size of diff23: {:.2f}MB".format(compressed_size(diff23)))
print("Compressed size of state3: {:.2f}MB".format(compressed_size(state3)))

Welches gibt mir:

Only 10% of elements change for state2
Compressed size of diff12: 0.90MB
Compressed size of state2: 7.20MB
All elements change by a small value for state3
Compressed size of diff23: 5.28MB
Compressed size of state3: 7.20MB
csiz
quelle
Nettes Beispiel. Hier spielt die Kompression eine Rolle.
Leopoldo Sanczyk
0

Wenn Ihre Position in vector3 gespeichert ist, kann die tatsächliche Entität jedoch nur einige Ganzzahlen gleichzeitig verschieben. Dann wäre es besser, das Delta in Bytes zu senden, als es in Ganzzahlen zu senden.

Aktuelle Position: 23009.0, 1.0, 2342.0 (3 float)
Neue Position: 23010.0, 1.0, 2341.0 (3 float)
Delta: 1, 0, -1 (3 byte)

Anstatt jedes Mal die genaue Position zu senden, senden wir das Delta.

the_lotus
quelle
0

Delta-Komprimierung ist eine Komprimierung von Delta-codierten Werten. Delta-Codierung ist eine Transformation, die eine unterschiedliche statistische Verteilung von Zahlen erzeugt. Wenn die Verteilung für den gewählten Komprimierungsalgorithmus günstig ist, wird die Datenmenge verringert. Es funktioniert sehr gut in einem System wie einem Spiel, in dem sich Entitäten zwischen zwei Aktualisierungen nur geringfügig bewegen.

Angenommen, Sie haben 100 Objekte in 2D. In einem großen Raster 512 x 512. Betrachtet man zum Beispiel nur ganze Zahlen. Das sind zwei ganze Zahlen pro Entität oder 200 Zahlen.

Zwischen zwei Aktualisierungen ändern sich alle unsere Positionen entweder um 0, 1, -1, 2 oder -2. Es gab 100 Instanzen von 0, 33 Instanzen von 1 und -1 und nur 17 Instanzen von 2 und -2. Das ist ziemlich häufig. Wir wählen Huffman-Codierung für die Komprimierung.

Der Huffman-Baum dafür wird sein:

 0  0
-1  100
 1  101
 2  110
-2  1110

Alle Ihre Nullen werden als ein einziges Bit codiert. Das sind nur 100 Bits. 66 Werte werden als 3 Bit und nur 34 Werte als 4 Bit codiert. Das sind 434 Bits oder 55 Bytes. Hinzu kommt ein kleiner Aufwand, um unseren Mapping-Baum zu retten, da der Baum winzig ist. Beachten Sie, dass Sie zum Codieren von 5 Zahlen 3 Bits benötigen. Wir haben hier die Fähigkeit getauscht, 1 Bit für '0' zu verwenden, um 4 Bits für '-2' zu verwenden.

Vergleichen Sie dies nun mit dem Senden von 200 beliebigen Zahlen. Wenn sich Ihre Entitäten nicht auf derselben Kachel befinden, ist fast garantiert, dass Sie eine schlechte statistische Verteilung erhalten. Der beste Fall wären 100 eindeutige Zahlen (alle auf demselben X mit unterschiedlichem Y). Das sind mindestens 7 Bits pro Zahl (175 Bytes) und sehr schwer für jeden Komprimierungsalgorithmus.

Die Delta-Komprimierung funktioniert im Sonderfall, wenn sich Ihre Entitäten nur geringfügig ändern. Wenn Sie viele eindeutige Änderungen vorgenommen haben, hilft die Delta-Codierung nicht.


Beachten Sie, dass Delta-Codierung und Komprimierung auch in anderen Situationen mit anderen Transformationen verwendet werden.

MPEG teilt das Bild in kleine Quadrate und wenn sich ein Teil des Bildes bewegt, werden nur die Bewegung und eine Änderung der Helligkeit gespeichert. In einem 25-fps-Film sind viele Änderungen zwischen Bildern sehr gering. Wieder Delta-Codierung + Komprimierung. Funktioniert am besten für statische Szenen.

MartinTeeVarga
quelle