Engine Support Systems Universität zu Köln – Historisch Kulturwissenschaftliche Informationsverarbeitung Softwaretechnologie II (Teil 2): Simulation und 3D Programmierung Dozent: Manfred Thaller, Dr. phil., Prof. Referentin: Julia Lohmann 27.06.2013 Inhalt • Subsystem Start-Up und Shut-Down • Memory Management • Container • Strings • Engine Konfiguration Julia Lohmann 2 Subsystem Start-Up/Shut-Down • Konfiguration und Initialisierung der Software – Spezielle Reihenfolge notwendig – Genauso beim Beenden • Initialisierungen – Aufruf ohne Reihenfolge vor main() • Destruktoren – Aufruf ohne Reihenfolge nach main() Unkontrollierte Aufrufe! Julia Lohmann 3 Subsystem Start-Up/Shut-Down • Einfache Lösung: – Explizite Konstruktoren und Destruktoren für jedes Einzelobjekt – Alle Funktionen außerhalb der main()-Funktion sind leer – 2 void-Funktionen in Konstruktoren und Destruktoren, um aus main() ausführbar zu sein Julia Lohmann 4 Memory Management • Optimierung der Speichernutzung: – Informationen in direkt benachbarten, kleinen Blöcken speichern – Fragmentierung der Informationen vermeiden Wichtig für Spielfluss! • Zu beachten: – Speicher wird nicht nur belegt, sondern auch wieder frei gegeben Julia Lohmann 5 Memory Management • Stack-Basiertes Vorgehen mit unterer Grenze: – Speicher wird bei Beginn eines Levels reserviert – Dynamische Speichernutzung ist minimal – Speicher wird nach Ende des Levels wieder komplett frei gegeben – Grenzadresse von belegt zu frei wird immer zwischengespeichert – Kann nur von „oben nach unten“ frei gegeben werden Julia Lohmann 6 Memory Management • Stack-Basiertes Vorgehen mit zwei Grenzen: – wie Stack-Basiertes Vorgehen, aber – Obere und untere Grenzadresse wird gespeichert – Kann „links“ von „oben nach unten“ und „rechts“ von „unten nach oben“ frei gegeben werden – Keine Probleme, solange nicht mehr gespeichert wird, als in der „Mitte“ zur Verfügung steht Julia Lohmann 7 Memory Management • Pool-Basiertes Vorgehen: – Reservieren von Speicherplatz durch Festlegung eines Pools von Speicherblöcken – Jeder Speicherblock ist gleich groß – Anlegen einer Liste für freie Speicherblöcke – Dynamisches Belegen und Freigeben problemlos möglich – Problem, falls mehr Speicherplatz nötig ist, als vorher festgelegt Julia Lohmann 8 Memory Management • Ausrichtungs-Basiertes Vorgehen: – Keine grundlegenden Begrenzungen – Adressen werden mit Freigabe von Speicher sofort angepasst – Jede Anpassung benötigt 1 Byte Speicherplatz zusätzlich, um jederzeit auffindbar zu bleiben Veränderung der Adresse in Bytes neue Adresse Julia Lohmann 9 Memory Management Julia Lohmann 10 Memory Management • Einzelframe-Speicherzuweisung: – Beschränkung eines Speichervorgehens auf die Zeit von einem Datenübertragungsblock – Während des Frames gefüllter Speicher, wird bei Ende des Frames von vorne befüllt – Freigabe des Speichers nicht notwendig – Unglaublich schnell – ABER: Speicher nur während des einen Frames verfügbar! Julia Lohmann 11 Memory Management • Doppelframe-Speicherzuweisung: – Zwei gleich große Einzelframe-Speicherzuweisungen – Zwischen jedem Frame wird zur jeweils anderen Speicherzuweisung immer hin und her gewechselt immer ein inaktiver Buffer zur Verfügung Informationen über zwei Frames hinweg verfügbar – Nach zwei Frames aber wieder Überschreiben der Informationen im vorher inaktiven Buffer Julia Lohmann 12 Memory Management • Fragmentierung: Verteilung der gespeicherten Daten, durch viele ungenutzte Bereiche • Speicherfehlschläge in „Zwischenräumen“ (selbst bei genug Speicher an zwei Stellen) • Aufteilen von einem Speicherblock, sodass er in mehrere Lücken passt • Stack- und Pool-Basierte Vorgehensweisen umgehen das Julia Lohmann 13 Memory Management • Problematik von Defragmentierung: – Verschieben von Speicherblöcken verursacht dass pointer in leere oder falsche Speicherblöcke zeigen • Anpassen aller pointer manuell manuelle Listenführung über alle pointer seitens des Programmierers zwingend nötig! • Nutzung von smart pointers oder handles Julia Lohmann 14 Memory Management • smart pointer – Klasse, die einen pointer enthält und sich insgesamt wie einer verhält Anlegen einer globalen Liste mit allen smart pointers • handles – Index in eine nicht verschiebbare Tabelle, die die pointer enthält Nur pointer müssen angepasst werden (handle ändert sich nicht; keine Änderung nötig für Objekte, die handles benutzen Julia Lohmann 15 Memory Management • Speicherinteraktion beeinflusst Performanz – CPU-Zugriff auf Informationen im Hauptspeicher am langsamsten – Cache als Speichermöglichkeit zwischen CPU und Hauptspeicher – CPU-Zugriff auf Informationen im Cache am schnellsten – Automatisches Laden eines Informationsteils vom Hauptspeicher in den Cache Julia Lohmann 16 Memory Management • Schlechte Performanz vermeiden: – Leistungsstarke Funktionen so kurz wie möglich halten – Funktionsaufrufe innerhalb essentieller Bereiche für die Performanz vermeiden – Aufgerufene Funktionen möglichst nah an der aufrufenden Funktion platzieren – Inline-Funktionen nur mit Bedacht benutzen Nötiger Zugriff auf Hauptspeicher minimal Julia Lohmann 17 Containers • container: – Beinhaltet und handhabt keine oder mehrere Datenelemente • Iteratoren, um Container systematisch zu durchsuchen – Besser preincrement nutzen („++i“ anstatt „i++“) kann direkt pointer/iterator hochzählen postincrement muss erst alten Wert in Cache schreiben, dort hochzählen lassen, dann wird der neue Wert zurück gegeben Julia Lohmann 18 Containers • Eigene Container-Strukturen implementieren – An Anforderungen der Software angepasst – Jederzeit Optimierung der Strukturen möglich – Jederzeit Anpassungen möglich – Abhängigkeit von Dritten verhindert • Implementationen von Dritten verwenden – standard template librarys (STL) bieten viel an – „robuste Implementationen“ durchaus verfügbar Julia Lohmann 19 Containers • Mögliche Nachteile von STL: – Dokumentation veraltet – Langsamer, da nicht an Software angepasst – Verbraucht mehr Speicher – Verwendet sehr häufig dynamische Speicherbelegung – Einbindung von Compiler zu Compiler verschieden Julia Lohmann 20 Containers • linked list: – Datenstruktur, die einem Array ähnelt und durch pointer zum nächsten/vorherigen Element damit verbunden ist – Elemente hinzufügen und entfernen sehr einfach – Möglichkeit nur pointer in eine Richtung zu verwenden, falls Benutzung wie Stack vorgesehen Julia Lohmann 21 Containers • extrusive list: – Datenstrukturen der Liste unabhängig von Datenstrukturen des Elements – Jedes Objekt der Liste beinhaltet einen pointer zum Element – Für jedes neue Objekt wird dynamisch neuer Speicher belegt beste Lösung: Pool-Basierte Vorgehensweise Julia Lohmann 22 Containers • intrusive list: – Datenstrukturen der Liste in Datenstrukturen des jeweiligen Elements eingebettet – Für jedes neue Objekt ist schon Speicher reserviert (keine dynamische Speicherung nötig) – Vererbung von Listeneigenschaften auf Elemente – pointer zurück auf das Element unnötig Julia Lohmann 23 Strings • Ein String ist ein Array von Zeichen – Entweder Länge vorher festlegen oder Speicher dynamisch benutzen • Übersetzungsproblematik beachten – Zeichendarstellung (äüöéèê etc.) – Lesedarstellung (rechts nach links und oben nach unten) – Übersetzter String möglicherweise wesentlich länger oder kürzer Julia Lohmann 24 Strings • String im Speicher finden und auslesen extrem zeitaufwändig, da mehrere Kopier- und Vergleichsvorgänge notwendig kurze, eindeutige Bezeichner oder besser Hashed-String-IDs verwenden (Umwandlung in Zahlen, da Vergleich wesentlich schneller) • UTF-8 (1 oder 2 Byte pro Zeichen) vs. UTF-16 (Immer 2 Byte pro Zeichen) Julia Lohmann 25 Engine Konfiguration • Einstellungsoptionen unweigerlich in großer Menge vorhanden – Manche für Spieler durch ein Menü veränderbar – Andere nur für Spieleentwickler, um Feinabstimmungen möglich zu machen versteckt oder bei Vervollständigung des Spiels herausgenommen Julia Lohmann 26 Engine Konfiguration • Möglichkeiten für Speichern und Laden – Textdateien – Komprimierte Binärdateien – Windows Registry – Kommandozeilen-Option – Umgebungsvariablen – Online Benutzerprofile Julia Lohmann 27