Sie müssen wissen, wo Sie die Null setzen sollen



Einige Optimierungen erfordern komplexe Datenstrukturen und Tausende von Codezeilen. In anderen Fällen führt eine ernsthafte Steigerung der Produktivität zu einer minimalen Änderung: Manchmal müssen Sie nur Null setzen. Dies ist wie eine alte Geschichte über einen Skipper, der den richtigen Ort kennt, um mit einem Hammer zu schlagen, und dem Kunden dann eine Rechnung stellt: 0,50 USD für einen Schlag auf das Ventil und 999,50 USD für das Wissen, wo er schlagen muss.

Ich persönlich habe mehrere Leistungsfehler festgestellt, die durch Eingabe einer Null behoben wurden, und in diesem Artikel möchte ich zwei Geschichten teilen.

Die Bedeutung der Messung


In den Tagen der ursprünglichen Xbox habe ich viele Spiele optimiert. In einem von ihnen wies der Profiler auf die Matrixtransformationsfunktion hin, die 7% der CPU-Zeit in Anspruch nahm - der größte Sprung im Diagramm. Deshalb habe ich mich fleißig daran gemacht, diese Funktion zu optimieren.

Es ist zu sehen, dass ich nicht der erste war, der dies versuchte. Die Funktion wurde bereits im Assembler neu geschrieben. Ich fand mehrere mögliche Verbesserungen in der Assemblersprache und versuchte, deren Wirkung zu messen. Dies ist ein wichtiger Schritt, ansonsten ist es einfach, eine „Optimierung“ durchzuführen, die nichts ändert oder die Situation sogar verschlechtert.

Die Messung war jedoch schwierig. Ich habe das Spiel gestartet, ein wenig mit paralleler Profilerstellung gespielt und dann das Profil studiert: Ist der Code schneller geworden? Es schien eine leichte Verbesserung zu geben, aber es war unmöglich, dies mit Sicherheit zu sagen.

Also habe ich die wissenschaftliche Methode angewendet. Er schrieb eine Sammlung von Tests, um alte und neue Codeversionen zu verwalten und Leistungsunterschiede genau zu messen. Dies dauerte nicht lange: Wie erwartet war der neue Code etwa 10% schneller als der alte.

Es stellte sich jedoch heraus, dass eine Beschleunigung von 10% Unsinn ist.

Es ist viel interessanter, dass der Testcode etwa zehnmal schneller ausgeführt wurde als im Spiel. Dies war eine aufregende Entdeckung.

Nachdem ich die Ergebnisse überprüft hatte, schaute ich eine Weile in die Leere, aber dann dämmerte es mir.

Caching-Rolle


Um Spielentwicklern die volle Kontrolle und maximale Leistung zu geben, können Sie mit Spielekonsolen Speicher mit verschiedenen Attributen zuweisen. Mit der ursprünglichen Xbox können Sie insbesondere nicht zwischenspeicherbaren Speicher zuweisen. Diese Art von Speicher (in der Tat die Art des Tags in Seitentabellen) ist nützlich, wenn Daten in die GPU geschrieben werden. Da der Speicher nicht zwischengespeichert wird, wird das Schreiben fast sofort ohne Verzögerungen oder Cache-Verunreinigungen mit „normaler“ Zuordnung in den Arbeitsspeicher übertragen.

Nicht zwischengespeicherter Speicher ist daher eine wichtige Optimierung, sollte jedoch sorgfältig verwendet werden. Insbesondere ist es äußerst wichtig, dass Spiele niemals versuchen, aus nicht zwischengespeicherten Speichern zu lesen , da sonst ihre Leistung erheblich abnimmt. Selbst die relativ langsame 733-MHz-CPU in der ursprünglichen Xbox benötigt eigene Caches, um eine ausreichende Leseleistung zu gewährleisten.

Jetzt wird klar, was passiert. Offensichtlich werden für diese Funktion Daten in einem nicht zwischengespeicherten Speicher zugewiesen, was zu einer geringen Leistung führt. Ein kleiner Test bestätigte diese Hypothese, daher ist es Zeit, das Problem zu beheben. Ich fand die Zeile, in der der Speicher zugewiesen ist, doppelklickte auf den Flag-Wert und zeigte auf Null.

Anstelle von ungefähr 7% der Prozessorzeit begann die Funktion ungefähr 0,7% zu verbrauchen und war kein Problem mehr.

Am Ende der Woche sah mein Bericht ungefähr so ​​aus: „39.999 Stunden Recherche, 0,001 Stunden Programmierung sind ein großer Erfolg!“

Entwickler müssen sich normalerweise nicht um die versehentliche Zuweisung von nicht zwischengespeichertem Speicher kümmern: Auf den meisten Betriebssystemen ist diese Option im Benutzerbereich mit Standardmethoden nicht verfügbar. Wenn Sie jedoch daran interessiert sind, wie viel nicht zwischenspeicherbarer Speicher das Programm verlangsamen kann, versuchen Sie es mit den Flags PAGE_NOCACHE oder PAGE_WRITECOMBINE in VirtualAlloc .

0 GiB ist besser als 4 GiB


Ich möchte dir eine andere Geschichte erzählen. Es geht um einen Fehler, den ich gefunden habe, und jemand anderes hat ihn behoben. Vor ein paar Jahren habe ich festgestellt, dass der Festplatten-Cache auf meinem Laptop zu oft geleert wird. Ich habe verfolgt, dass dies passiert, wenn die 4-GiB-Linie erreicht ist, und am Ende stellte sich heraus, dass der Treiber für meine neue Backup-Festplatte SectorSize auf 0xFFFFFFFF (oder -1) setzt, wenn auf eine unbekannte Sektorgröße verwiesen wird. Der Windows-Kernel interpretiert diesen Wert als 4 GiB und weist den entsprechenden Speicherblock zu, der das Problem verursacht hat.

Ich habe keine Kontakte in Western Digital, aber ich kann davon ausgehen, dass sie diesen Fehler behoben haben, indem sie die Konstante 0xFFFFFFFF (oder -1) durch Null ersetzt haben. Ein Zeichen wurde eingegeben - und ein ernstes Leistungsproblem behoben.

(Weitere Informationen zu dieser Studie finden Sie im Artikel „Verlangsamen von Windows: Erkunden und Identifizieren“. )

Beobachtungen


  • In beiden Fällen liegt das Problem beim Caching
  • Entscheidend war die Verwendung eines Profilers, um das Problem zu lokalisieren.
  • Wenn der Patch nicht durch Messungen überprüft wird, hilft dies nicht unbedingt.
  • Ich könnte über viele andere solche Fälle schreiben, aber sie sind entweder zu geheim oder zu langweilig.
  • Die richtige Entscheidung muss nicht kompliziert sein. Manchmal führt eine große Verbesserung zu einer kleinen Änderung. Alles was Sie wissen müssen ist wo

Ich habe den Code durch Auskommentieren von #define und durch andere triviale Änderungen optimiert. Sagen Sie uns in den Kommentaren, ob Sie solche Geschichten haben.

Source: https://habr.com/ru/post/de434056/


All Articles