Profilerstellung für Ihr WebGL-Spiel mit dem Flag about:tracing

Lilli Thompson
Lilli Thompson

Wenn Sie sie nicht messen können, können Sie sie auch nicht verbessern.

Lord Kelvin

Damit Ihre HTML5-Spiele schneller laufen, müssen Sie zuerst die Leistungsengpässe ermitteln. Das kann jedoch schwierig sein. Das Auswerten der Daten zu Frames pro Sekunde (fps) ist ein Anfang, aber um ein vollständiges Bild zu erhalten, müssen Sie die Nuancen der Chrome-Aktivitäten verstehen.

Das „about:tracing“-Tool bietet Informationen, mit denen Sie vorschnelle Problemumgehungen zur Leistungsverbesserung vermeiden können, die aber im Wesentlichen fundierte Vermutungen darstellen. So sparst du viel Zeit und Energie, bekommst ein klareres Bild davon, was Chrome mit jedem Frame macht, und nutzt diese Informationen zur Optimierung deines Spiels.

Hallo about:tracing

Das Chrome-Tool „about:Tracing“ bietet Ihnen einen Einblick in alle Aktivitäten von Chrome über einen bestimmten Zeitraum mit einer so detaillierten Darstellung, dass Sie ihn auf den ersten Blick vielleicht als etwas überfordernd empfinden. Viele Funktionen in Chrome sind für das sofortige Tracing instrumentiert, sodass Sie ohne manuelle Instrumentierung trotzdem die Leistung über „about:tracing“ verfolgen können. Weitere Informationen finden Sie in einem späteren Abschnitt zur manuellen Instrumentierung Ihres JS-Codes.

Geben Sie zum Aufrufen der Tracing-Ansicht einfach "about:tracing" ein. in die Omnibox (Adressleiste) von Chrome.

<ph type="x-smartling-placeholder">
</ph> Chrome-Omnibox
Geben Sie „about:tracing“ ein. in die Omnibox von Chrome

Über das Tracing-Tool können Sie die Aufzeichnung starten, Ihr Spiel einige Sekunden lang ausführen und sich dann die Trace-Daten ansehen. Hier sehen Sie ein Beispiel dafür, wie die Daten aussehen könnten:

<ph type="x-smartling-placeholder">
</ph> Einfaches Tracing-Ergebnis
Einfaches Tracing-Ergebnis

Ja, das ist verwirrend. Sehen wir uns an, wie man sie liest.

Jede Zeile repräsentiert einen Prozess, der gerade analysiert wird, die linke rechte Achse gibt die Zeit an und jedes farbige Feld ist ein instrumentierter Funktionsaufruf. Es gibt Zeilen für eine Reihe verschiedener Arten von Ressourcen. Am interessantesten für die Profilerstellung sind CrGpuMain, das heißt, was der Graphics Processing Unit (GPU) tut, und CrRendererMain. Jeder Trace enthält CrRendererMain-Zeilen für jeden geöffneten Tab während des Trace-Zeitraums, einschließlich des Tabs about:tracing selbst.

Beim Lesen von Trace-Daten besteht Ihre erste Aufgabe darin, zu ermitteln, welche CrRendererMain-Zeile Ihrem Spiel entspricht.

<ph type="x-smartling-placeholder">
</ph> Einfaches Tracing-Ergebnis hervorgehoben
Ergebnis für einfaches Tracing hervorgehoben

In diesem Beispiel sind die beiden Kandidaten 2216 und 6516. Leider gibt es derzeit keine ausgefeilte Möglichkeit, Ihre Anwendung auszuwählen, außer nach der Zeile zu suchen, die viele regelmäßige Updates durchführt (oder wenn Sie Ihren Code manuell mit Trace-Punkten instrumentiert haben, um nach der Zeile zu suchen, die Ihre Trace-Daten enthält). In diesem Beispiel läuft offenbar eine Hauptschleife von 6516 über die Häufigkeit von Updates ab. Wenn Sie alle anderen Tabs schließen, bevor Sie mit dem Trace beginnen, wird es einfacher sein, das richtige CrRendererMain herauszufinden. Es kann jedoch immer noch CrRendererMain-Zeilen für andere Prozesse als Ihr Spiel geben.

Frame wird gesucht

Sobald Sie im Ablaufverfolgungstool die richtige Zeile für Ihr Spiel gefunden haben, suchen Sie im nächsten Schritt die Hauptschleife. Die Hauptschleife sieht wie ein sich wiederholendes Muster in den Ablaufverfolgungsdaten aus. Sie können durch die Ablaufverfolgungsdaten navigieren, indem Sie die Tasten W, A, S und D verwenden: Mit A und D bewegen Sie sich im Zeitverlauf nach links oder rechts und mit W und S können Sie die Daten heran- und herauszoomen. Wenn Ihr Spiel bei 60 Hz läuft, würden Sie erwarten, dass Ihre Hauptschleife ein Muster ist, das sich alle 16 Millisekunden wiederholt.

<ph type="x-smartling-placeholder">
</ph> Sieht aus wie drei Ausführungsframes
Sieht aus wie drei Ausführungsframes

Sobald Sie den Heartbeat Ihres Spiels gefunden haben, können Sie herausfinden, was genau Ihr Code in jedem Frame tut. Verwenden Sie W, A, S und D, um heranzuzoomen, bis Sie den Text in den Funktionsfeldern lesen können.

<ph type="x-smartling-placeholder">
</ph> Tief in einen Ausführungsframe hinein
Tief in einen Ausführungsframe

Diese Sammlung von Feldern zeigt eine Reihe von Funktionsaufrufen, wobei jeder Aufruf durch ein farbiges Feld dargestellt wird. Jede Funktion wurde von dem Feld darüber aufgerufen. In diesem Fall sehen Sie, dass MessageLoop::RunTask namens RenderWidget::OnSwapBuffersComplete aufgerufen wurde, die wiederum RenderWidget::DoDeferredUpdate aufgerufen hat und so weiter. Durch das Lesen dieser Daten erhalten Sie einen vollständigen Überblick darüber, wie und wie lange jede Ausführung gedauert hat.

An dieser Stelle wird es jedoch etwas haftend. Bei den Informationen über „about:tracing“ handelt es sich um die unverarbeiteten Funktionsaufrufe aus dem Chrome-Quellcode. Anhand des Namens lassen sich fundierte Vermutungen darüber anstellen, was die einzelnen Funktionen bewirken. Die Informationen sind jedoch nicht unbedingt benutzerfreundlich. Es ist hilfreich, den Gesamtfluss Ihres Frames zu sehen, aber Sie benötigen etwas, das für Menschen lesbar ist, um das Problem zu ermitteln.

Trace-Tags hinzufügen

Es gibt aber eine einfache Möglichkeit, Ihrem Code eine manuelle Instrumentierung hinzuzufügen, um Trace-Daten zu erstellen: console.time und console.timeEnd.

console.time("update");
update();
console.timeEnd("update");
console.time("render");
update();
console.timeEnd("render");

Mit dem obigen Code werden im Namen der Ablaufverfolgungsansicht neue Felder mit den angegebenen Tags erstellt. Wenn Sie die App erneut ausführen, wird also "Aktualisieren" angezeigt. und „rendern“, die die Zeit anzeigen, die zwischen dem Start- und dem Endaufruf für jedes Tag verstrichen ist.

<ph type="x-smartling-placeholder">
</ph> Manuell hinzugefügte Tags
Manuell hinzugefügte Tags

Damit können Sie für Menschen lesbare Tracing-Daten erstellen, um Hotspots in Ihrem Code zu verfolgen.

GPU oder CPU?

Bei hardwarebeschleunigter Grafik ist eine der wichtigsten Fragen, die Sie sich bei der Profilerstellung stellen können, folgende: Ist dieser Code GPU- oder CPU-gebunden? Mit jedem Frame führen Sie Rendering auf der GPU und einige Logik auf der CPU aus. Um zu verstehen, warum Ihr Spiel langsam ist, müssen Sie überprüfen, wie die Arbeit auf die beiden Ressourcen verteilt ist.

Suchen Sie zuerst in der Tracing-Ansicht nach der Zeile CrGPUMain, die anzeigt, ob die GPU zu einem bestimmten Zeitpunkt ausgelastet ist.

GPU- und CPU-Traces

Wie Sie sehen, verursacht jeder Frame Ihres Spiels CPU-Leistung sowohl im CrRendererMain- als auch auf der GPU. Der obige Trace zeigt einen sehr einfachen Anwendungsfall, bei dem sowohl die CPU als auch die GPU für jeden 16-ms-Frame die meiste Zeit inaktiv sind.

Die Tracing-Ansicht ist besonders hilfreich, wenn Sie ein Spiel haben, das langsam läuft und Sie sich nicht sicher sind, welche Ressourcen Sie maximal ausgeschöpft haben. Der Schlüssel zur Fehlerbehebung ist es, die Verbindung zwischen GPU und CPU zu prüfen. Nehmen Sie das gleiche Beispiel wie zuvor, aber fügen Sie in der Aktualisierungsschleife ein wenig zusätzlichen Aufwand hinzu.

console.time("update");
doExtraWork();
update(Math.min(50, now - time));
console.timeEnd("update");

console.time("render");
render();
console.timeEnd("render");

Jetzt sehen Sie einen Trace, der so aussieht:

GPU- und CPU-Traces

Was sagt dieser Trace aus? Wie wir sehen, reicht der abgebildete Frame von etwa 2270 ms bis 2320 ms, was bedeutet, dass jeder Frame etwa 50 ms benötigt (eine Framerate von 20 Hz). Neben dem Update-Feld sind Aufteilungen von farbigen Feldern zu sehen, die die Rendering-Funktion darstellen, aber der Frame wird vollständig von der Aktualisierung selbst dominiert.

Im Gegensatz zu den Vorgänge auf der CPU ist die GPU für die meisten Frames immer noch inaktiv. Zur Optimierung dieses Codes könnten Sie nach Vorgängen suchen, die in Shader-Code ausgeführt werden können, und sie auf die GPU verschieben, um Ressourcen optimal zu nutzen.

Was passiert, wenn der Shader-Code selbst langsam und die GPU überlastet ist? Was ist, wenn wir die unnötige Arbeit von der CPU entfernen und stattdessen einige Arbeit in den Fragment-Shader-Code einfügen? Hier ist ein unnötig teurer Fragment-Shader:

#ifdef GL_ES
precision highp float;
#endif
void main(void) {
  for(int i=0; i<9999; i++) {
    gl_FragColor = vec4(1.0, 0, 0, 1.0);
  }
}

Wie sieht eine Code-Spur mit diesem Shader aus?

<ph type="x-smartling-placeholder">
</ph> GPU- und CPU-Traces bei Verwendung von langsamem GPU-Code
GPU- und CPU-Traces bei Verwendung von langsamem GPU-Code

Notieren Sie sich auch hier die Dauer eines Frames. Hier reicht das Wiederholungsmuster von etwa 2.750 ms bis 2.950 ms, eine Dauer von 200 ms (Framerate von etwa 5 Hz). Die Zeile CrRendererMain ist fast vollständig leer. Das bedeutet, dass die CPU die meiste Zeit inaktiv ist, während die GPU überlastet ist. Das ist ein Zeichen dafür, dass deine Shader zu schwer sind.

Wenn Sie nicht genau wissen, was die niedrige Framerate verursacht hat, könnten Sie das 5-Hz-Update beobachten und versucht sein, den Spielcode aufzurufen und die Spiellogik zu optimieren oder zu entfernen. In diesem Fall würde das absolut nichts nützen, denn die Logik in der Spielschleife ist nicht das, was Zeit verschlingt. Dieser Trace zeigt sogar an, dass die Ausführung von mehr CPU-Arbeit mit jedem Frame praktisch „kostenlos“ wäre. da die CPU im Leerlauf ist und mehr Arbeit zugewiesen wird, hat dies keinen Einfluss auf die Dauer des Frames.

Reale Beispiele

Schauen wir uns nun an, wie das Tracing von Daten aus einem echten Spiel aussieht. Das Tolle an Spielen, die mit offenen Webtechnologien entwickelt wurden, ist, dass man sehen kann, was in deinen Lieblingsprodukten passiert. Wenn Sie Profilerstellungstools testen möchten, können Sie Ihren bevorzugten WebGL-Titel aus dem Chrome Web Store auswählen und ein Profil mit about:tracing erstellen. Dies ist ein Beispiel-Trace aus dem ausgezeichneten WebGL-Spiel Skid Racer.

<ph type="x-smartling-placeholder">
</ph> Ein echtes Spiel auf der Spur
Ein echtes Spiel auf der Spur

Anscheinend dauert jeder Frame etwa 20 ms, was bedeutet, dass die Framerate etwa 50 fps beträgt. Sie sehen, dass die Arbeit zwischen CPU und GPU ausgeglichen ist, aber die GPU ist die Ressource, die am meisten gefragt ist. Wenn Sie sehen möchten, wie es ist, ein Profil aus echten WebGL-Spielen zu erstellen, probieren Sie einige der mit WebGL erstellten Chrome Web Store-Titel aus, z. B.:

Fazit

Wenn Sie möchten, dass Ihr Spiel bei 60 Hz läuft, müssen alle Ihre Vorgänge für jeden Frame in 16 ms CPU- und 16 ms GPU-Zeit passen. Sie haben zwei Ressourcen, die parallel genutzt werden können, und Sie können Aufgaben zwischen ihnen verlagern, um die Leistung zu maximieren. „about:tracing View“ ist ein unschätzbares Tool, um einen Einblick in die tatsächliche Funktionsweise Ihres Codes zu erhalten. Sie hilft Ihnen, Ihre Entwicklungszeit zu maximieren, indem Sie die richtigen Probleme angehen.

Nächste Schritte

Neben der GPU können Sie auch andere Teile der Chrome-Laufzeit verfolgen. Chrome Canary, die frühe Version von Chrome, dient zur Verfolgung von E/A, IndexedDB und anderen Aktivitäten. In diesem Chromium-Artikel finden Sie weitere Informationen zum aktuellen Status der Ereignisverfolgung.

Wenn Sie Webspielentwickler sind, sollten Sie sich das Video unten ansehen. Es ist eine Präsentation des Game Developer Advocate-Teams von Google bei der GDC 2012 zur Leistungsoptimierung für Chrome-Spiele: