Ich habe nachgeforscht, wie ich meine eigenen Zuweisungsmethoden erstellen kann (die Dinge wie Speicherpool und Profilerstellung unterstützen), aber während ich meine Nachforschungen fortsetze, habe ich nach Möglichkeiten gesucht, wie dies in der Spieleentwicklung gemacht wurde.
Welche Speicherzuweisungstechnik könnte ich verwenden, und warum ist es eine gute Technik?
Antworten:
Die Game Engine-Architektur enthält einige Informationen zu diesem Thema. Die Grundlagen sind, dass Sie einige Analysen durchführen müssen, um zu verstehen, was Ihr Speicherbedarf pro Ebene / Frame / etc. sind wie, aber es gibt ein paar Muster, die der Autor mehrfach gesehen hat:
Das Wichtigste, worauf der Autor achten sollte, ist die Fragmentierung des Gedächtnisses. Dies ist weniger problematisch, wenn Sie z. B. für einen PC entwickeln, auf dem Sie eine Art Paging-Sicherung haben, auf die Sie sich verlassen können. In einem festen Speicherkontext wie einer Konsole besteht jedoch das Risiko, dass nicht genügend Arbeitsspeicher zur Verfügung steht. Wenn Sie versuchen, ein großes Objekt zuzuweisen, da Ihr Speicher so fragmentiert ist, dass nur kleine zusammenhängende Blöcke verfügbar sind. Zu diesem Zweck empfiehlt er, dass ein stapelbasierter Allokator wie oben auch eine Methode zum periodischen Defragmentieren seines Inhalts enthält.
Für weitere Informationen zum eigentlichen Code empfehle ich Christian Gyrlings Artikel "Sind wir nicht mehr im Gedächtnis?". Hier werden Techniken für benutzerdefinierte Zuweiser behandelt, hauptsächlich aus der Perspektive der Analyse von Speichernutzungsmustern. Dies gilt jedoch auch für die Entwicklung einer benutzerdefinierten Lösung für die Speicherverwaltung.
quelle
Nach allem, was ich gesehen (aber nicht getan) habe, erbt jedes Spiel die Zuweisungsmechanismen entweder von einem Framework, von einer Game Engine, von der Vorgängerversion (2010 -> 2011), oder es werden eine Reihe neuer, speziell für dieses Framework geschriebener Mechanismen erstellt Struktur (entweder wenn Datenstrukturen wiederverwendbar und von fester Größe sind oder von zahlreichen Typen und variablen Größen).
Außerdem hatten wir andere Zuordnungen für Sounddateien / Komponenten als für Levels und andere Spielobjekte im selben Projekt. In anderen Projekten werden Zuweiser nur für die von dieser Bibliothek verwalteten Komponenten von externen Bibliotheken geerbt.
Die Optimierung hängt wirklich von Ihren Bedürfnissen ab. In der Regel erfolgt die Zuordnung jedoch vor dem Eintritt in die Spielszene, und anschließend wird der Speicher wieder verwendet. Bei einigen Spielen müssen keine benutzerdefinierten Zuweisungen vorgenommen werden. Bei Actionspielen, bei denen Prozessor-, Speicher- und Datenressourcen budgetiert sind, können Sie es sich nicht leisten, bei großen Zuweisungen Verarbeitungszeit zu verlieren, und Sie können keinen Speicher für Fragmentierung und andere Probleme verschwenden.
In Bezug auf Beispiele sollten Sie zunächst einen Blick auf die OGRE 3D- Game-Engine werfen, die einige Optionen zum Konfigurieren von Speicherzuordnungen bietet .
quelle
Der Fehler, der häufig gemacht wird, besteht darin, Ihre eigenen Allokatoren zu schreiben, damit Sie mehr Kontrolle darüber haben, wie viel Speicher von jedem System verwendet wird, und mehr Einblick in die aktuellen Vorgänge haben. Ein viel besserer Weg, dies zu erreichen, ist die Verwendung eines Speicherprofilers. Es gibt viele Speicher-Profiler, mein Profiler MemPro ist ein Beispiel. Dies ist eine völlig nicht-invasive Methode, um die Speichernutzung im Auge zu behalten, und Sie können sie mithilfe von Callstack-Platzhalterfiltern automatisch in Untersysteme aufteilen. Im Idealfall ist es am besten, die Speicherzuordnung und die Speicherverfolgung vollständig getrennt zu halten, da sie ganz unterschiedliche Anforderungen haben.
Die willkürliche Aufteilung Ihres Speichers in Pools kann oft nachteilig sein, da jeder Pool einen Overhead hat. Sie können am Ende viel mehr Speicher verwenden, als Sie benötigen, ohne es zu bemerken. Um die Verschwendung zu reduzieren, ist es immer besser, alles zusammenzufassen, der Spielraum wird dann vom gesamten System geteilt.
Die einzigen Gründe für die Verwendung benutzerdefinierter Zuordnungen sind die CPU-Leistung (hauptsächlich aus Gründen der Cache-Kohärenz) und die Begrenzung der Fragmentierung. Ein perfektes Beispiel dafür ist ein Partikelsystem. Sie möchten, dass alle Partikel im Speicher zusammenhängend sind, und Sie möchten den Hauptspeicher nicht mit vielen kurzlebigen Zuweisungen überschütten. Ein weiteres gutes Beispiel für das Abschotten ist eine Skriptsprache.
Wenn Sie ein Beispiel für einen Allzweck-Malloc-Ersatz wünschen, können Sie sich meinen VMem-Allokator ansehen . Es wurde in einer Reihe von ausgelieferten AAA-Spielen verwendet. Es verfügt über Techniken, die die Fragmentierung begrenzen und den Speicherbedarf gering halten, was für Konsolenspiele von entscheidender Bedeutung ist. Es ist auch sehr schnell unter hohem Thread-Konflikt. Meine Website enthält umfangreiche Dokumentationen zu diesen Techniken.
quelle