System-Tracing konfigurieren

Sie können System-Tracing so konfigurieren, dass ein erfasster CPU- und Thread-Profil Ihrer App über einen kurzen Zeitraum. Dann können Sie den Ausgabebericht eines System-Trace verwenden, um die die Leistung.

Spielbasiertes System-Trace einrichten

Das Systrace-Tool ist auf zwei Arten verfügbar:

Systrace ist ein Low-Level-Tool, das:

  • Liefert Grundwahrheit Systrace erfasst die Ausgabe direkt aus dem Kernel, Daher sind die erfassten Messwerte fast identisch mit denen, Systemaufrufe erfasst.
  • Verbraucht nur wenige Ressourcen. Systrace sorgt für einen sehr geringen Overhead auf dem in der Regel weniger als 1%, da dabei Daten in einen Zwischenspeicher im Arbeitsspeicher gestreamt werden.

Optimale Einstellungen

Es ist wichtig, dem Tool einen sinnvollen Satz von Argumenten zu geben:

  • Kategorien:Die besten Kategorien für ein spielbasiertes System Traces sind: {sched, freq, idle, am, wm, gfx, view, sync, binder_driver, hal, dalvik}.
  • Puffergröße:Als allgemeine Regel gilt eine Puffergröße von 10 MB pro CPU-Kern. ein etwa 20 Sekunden langes Trace. Wenn ein Gerät z. B. eine Zwei Quad-Core-CPUs (insgesamt 8 Kerne), ein geeigneter Wert, der in den Das systrace-Programm ist 80.000 KB (80 MB) groß.

    Wenn in Ihrem Spiel häufig der Kontext gewechselt wird, erhöhen Sie den Zwischenspeicher auf 15 MB pro CPU-Kern.

  • Benutzerdefinierte Ereignisse:Wenn Sie benutzerdefinierte Ereignisse zum Erfassen von Ereignissen in Ihrem Spiel, aktivieren Sie Das Flag -a, mit dem Systrace diese benutzerdefinierten Ereignisse in den Ausgabebericht.

Mit der systrace-Befehlszeile verwenden Sie den folgenden Befehl, um einen System-Trace zu erfassen, der Best Practices für Kategoriesatz, Zwischenspeicher Größen und benutzerdefinierte Ereignisse:

python systrace.py -a com.example.myapp -b 80000 -o my_systrace_report.html \
  sched freq idle am wm gfx view sync binder_driver hal dalvik

Wenn Sie die Systrace-System-App auf einem führen Sie die folgenden Schritte aus, System-Trace erfassen, bei dem Best Practices für Kategoriesatz, Zwischenspeicher Größen und benutzerdefinierte Ereignisse:

  1. Aktivieren Sie die Option Debug-fähige Anwendungen verfolgen.

    Bis muss das Gerät 256 MB oder 512 MB frei (je nach ob die CPU 4 oder 8 Kerne hat), und jeder 64-MB-Speicher als zusammenhängender Block verfügbar sein.

  2. Wählen Sie Kategorien aus und aktivieren Sie die Kategorien in der folgenden Liste:

    • am: Aktivitätsmanager
    • binder_driver: Binder-Kernel-Treiber
    • dalvik: Dalvik-VM
    • freq: CPU-Frequenz
    • gfx: Grafik
    • hal: Hardwaremodule
    • idle: CPU-Leerlauf
    • sched: CPU-Planung
    • sync: Synchronisierung
    • view: System ansehen
    • wm: Fenstermanager
  3. Aktivieren Sie Datensatz-Tracing.

  4. Lade dein Spiel.

  5. Führen Sie die Interaktionen in Ihrem Spiel durch, die zu dem Spiel passen, dessen Geräteleistung, die Sie messen möchten.

  6. Schalten Sie das System ein, sobald Sie unerwünschtes Verhalten festgestellt haben. Nachzeichnen aus.

Sie haben die Leistungsstatistiken erfasst um das Problem weiter zu analysieren.

Bei System-Traces auf dem Gerät werden Dateien in einem komprimierten Trace gespeichert, um Speicherplatz zu sparen Format (*.ctrace). Um diese Datei beim Erstellen eines Berichts zu dekomprimieren, verwenden Sie den Befehlszeilenprogramm und fügen Sie die Option --from-file ein:

python systrace.py --from-file=/data/local/traces/my_game_trace.ctrace \
  -o my_systrace_report.html

Bestimmte Leistungsbereiche verbessern

In diesem Abschnitt werden einige häufige Leistungsprobleme bei Spielen für Mobilgeräte und beschreibt, wie du diese Aspekte deines Spiels identifizieren und verbessern kannst.

Ladegeschwindigkeit

Die Spieler möchten so schnell wie möglich mitmachen. um die Ladezeiten Ihres Spiels zu verbessern. Die folgenden Messwerte helfen in der Regel bei den Ladezeiten:

  • Führen Sie ein Lazy Loading durch. Wenn Sie dieselben Assets in mehreren aufeinanderfolgenden Szenen oder Levels in Ihrem Spiel zu erstellen, laden Sie diese Assets nur einmal.
  • Reduzieren Sie die Größe Ihrer Assets. So können Sie die Gruppierung unkomprimiert Versionen dieser Assets mit dem APK Ihres Spiels hinzufügen.
  • Verwenden Sie eine platteneffiziente Komprimierungsmethode. Ein Beispiel für eine solche Methode ist zlib verwenden.
  • Verwenden Sie IL2CPP. statt Mono. Dies gilt nur, wenn Sie Unity verwenden. IL2CPP bietet eine bessere für die Ausführung von C#-Skripts.
  • Multithread-Spiele für dein Spiel erstellen Weitere Informationen findest du in der Frame-Rate Konsistenz.

Framerate-Konsistenz

Eines der wichtigsten Elemente des Spielerlebnisses ist das Erreichen einer eine konstante Framerate zu erhalten. Um dieses Ziel leichter zu erreichen, befolgen Sie die die in diesem Abschnitt erläutert werden.

Multithreading

Wenn Sie für mehrere Plattformen entwickeln, innerhalb deines Spiels in einem einzigen Thread verpacken. Obwohl diese Ausführungsmethode einfach ist, in vielen Spiel-Engines zu implementieren ist, ist sie unter Android Geräte. Daher laden Single-Threaded-Spiele oft langsam und es fehlt eine konstante Framerate zu erhalten.

Die in Abbildung 1 gezeigte Systrace-Funktion zeigt ein für ein Spiel typisches Verhalten. jeweils nur auf einer CPU ausgeführt werden:

Diagramm der Threads
in einem System-Trace

<ph type="x-smartling-placeholder">
</ph> Abbildung 1: Systrace-Bericht für ein Single-Threaded-Spiel

Wenn Sie die Leistung Ihres Spiels verbessern möchten, erstellen Sie ein Multithread-Game. Normalerweise sollten Sie zwei Threads verwenden:

  • Einen Spielthread, der die Hauptmodule Ihres Spiels enthält und das Rendering sendet .
  • Rendering-Thread, der Renderingbefehle empfängt und sie in Grafikbefehle, mit denen die GPU eines Geräts eine Szene darstellen kann.

Die Vulkan API erweitert dieses Modell, da sie zwei gängige und puffert. Mit dieser Funktion können Sie mehrere Rendering- Threads über mehrere CPUs hinweg, wodurch die Renderingzeit einer Szene weiter verbessert wird.

Sie können auch einige suchmaschinenspezifische Änderungen vornehmen, um die Multithreading-Leistung:

  • Wenn du dein Spiel mit der Unity-Spiel-Engine entwickelst, aktiviere die Optionen für Multithreaded-Rendering und GPU Skinning
  • Wenn Sie ein benutzerdefiniertes Rendering-Modul verwenden, muss der Rendering-Befehl Pipeline und Grafikbefehl-Pipeline sind korrekt ausgerichtet; Andernfalls kann es bei der Darstellung der Szenen Ihres Spiels zu Verzögerungen kommen.

Nachdem Sie diese Änderungen angewendet haben, sollte Ihr Spiel mindestens zwei CPUs belegen wie in Abbildung 2 dargestellt:

Diagramm der Threads
in einem System-Trace

<ph type="x-smartling-placeholder">
</ph> Abbildung 2: Systrace-Bericht für ein Multithread-Spiel

UI-Element wird geladen

Diagramm eines Frames
  Stack in einem System-Trace <ph type="x-smartling-placeholder">
</ph> Abbildung 3: Systrace-Bericht für ein Spiel, das Dutzende von UI-Elementen rendert Elemente gleichzeitig

Bei der Entwicklung eines Spiels mit vielen Funktionen ist es verlockend, viele verschiedene Optionen und Aktionen für den Spieler gleichzeitig. Damit die Framerate gleich bleibt, Es ist jedoch wichtig, die relativ kleine Größe von mobilen Displays zu berücksichtigen. und die Benutzeroberfläche so einfach wie möglich halten.

Der in Abbildung 3 gezeigte Systrace-Bericht ist ein Beispiel für einen UI-Frame, der zu viele Elemente relativ zum Funktionen.

Es empfiehlt sich, die Aktualisierungszeit der Benutzeroberfläche auf 2 bis 3 Millisekunden zu verkürzen. Sie können um solche schnellen Updates zu erzielen, indem Optimierungen wie die folgenden durchgeführt werden:

  • Nur die Elemente auf dem Bildschirm aktualisieren, die verschoben wurden
  • Begrenzen Sie die Anzahl der UI-Texturen und -Ebenen. Kombinieren Sie Grafikaufrufe, wie Shader und Texturen, für die dasselbe Material verwendet wird.
  • Animationsvorgänge für Elemente auf die GPU verschieben.
  • Sie führt eine aggressivere Herumsortierung und Verdeckungsbeseitigung durch.
  • Führe Zeichenvorgänge nach Möglichkeit mit der Vulkan API aus. Der Unentschiedenaufruf ist auf Vulkan geringer.

Energieverbrauch

Auch nachdem Sie die im vorherigen Abschnitt besprochenen Optimierungen vorgenommen haben, können Sie dass sich die Framerate deines Spiels innerhalb der ersten 45 bis 50 Minuten Gameplay. Außerdem kann sich das Gerät erwärmen und mehr Energie verbrauchen, im Laufe der Zeit.

In vielen Fällen sind diese unerwünschten Anteile an Wärme und Stromverbrauch wie die Arbeitslast Ihres Spiels auf die CPUs eines Geräts verteilt ist. Zum Erhöhen Energieverbrauchseffizienz Ihres Spiels messen, wenden Sie die Best Practices folgenden Abschnitten.

Speicherlastige Threads auf einer CPU belassen

Auf vielen Mobilgeräten befinden sich die L1-Caches auf bestimmten CPUs und die L2-Caches. basieren auf einer Gruppe von CPUs, die einen gemeinsamen Takt haben. Um die L1-Cache-Treffer zu maximieren, In der Regel ist es am besten, den Hauptthread deines Spiels sowie alle anderen speicherlastigen Threads, die auf einer einzigen CPU ausgeführt werden.

Kurzfristige Arbeiten auf CPUs mit geringerer Leistung verschieben

Die meisten Spiel-Engines, einschließlich Unity, wissen, dass sie Worker-Thread-Vorgänge auf eine andere CPU im Verhältnis zum Hauptthread Ihres Spiels. Das Modul ist jedoch nicht die spezifische Architektur eines Geräts erkennt und die die Arbeitsbelastung so gut wie möglich zu bewältigen.

Die meisten System-on-a-Chip-Geräte haben mindestens zwei gemeinsame Uhren, eine für die mit den schnellen CPUs und eine mit den langsamen CPUs. Eine Folge davon Architektur besteht darin, dass, wenn eine schnelle CPU mit maximaler Geschwindigkeit betrieben werden muss, alle andere schnelle CPUs mit maximaler Geschwindigkeit arbeiten.

Der Beispielbericht in Abbildung 4 zeigt ein Spiel, das schnelle CPUs. Bei diesem hohen Aktivitätsniveau wird jedoch viel Energie und Wärme erzeugt. schnell ändern.

Diagramm der Threads
in einem System-Trace

<ph type="x-smartling-placeholder">
</ph> Abbildung 4: Systrace-Bericht mit einer suboptimalen Zuweisung von Threads zu die CPUs des Geräts

Um den Stromverbrauch insgesamt zu reduzieren, sollten Sie dem Planer vorschlagen, kürzere Arbeitsschritte wie das Laden von Audio, das Ausführen von Worker-Threads die Ausführung des Choreografen – auf die langsamen CPUs auf einem Gerät zurückgegriffen werden. Übertragen Sie möglichst viel von dieser Arbeit auf die langsamen CPUs und halten Sie dabei gewünschte Framerate an.

Bei den meisten Geräten werden die langsamen CPUs vor den schnellen CPUs aufgeführt. Sie können jedoch nicht davon ausgehen, dass das Sicherheitscenter Ihres Geräts diese Reihenfolge verwendet. Führen Sie zur Überprüfung ähnliche Befehle wie die aus, um dies zu prüfen. in dieser CPU-Topologie-Erkennung angezeigt Code auf GitHub.

Sobald Sie wissen, welche CPUs die langsamen CPUs Ihres Geräts sind, können Sie diese gemeinsamen Interessen für Ihre kurzzeitigen Threads, die der Planer des Geräts verwendet, folgt. Fügen Sie dazu den folgenden Code in jeden Thread ein:

#include <sched.h>
#include <sys/types.h>
#include <unistd.h>

pid_t my_pid; // PID of the process containing your thread.

// Assumes that cpu0, cpu1, cpu2, and cpu3 are the "slow CPUs".
cpu_set_t my_cpu_set;
CPU_ZERO(&my_cpu_set);
CPU_SET(0, &my_cpu_set);
CPU_SET(1, &my_cpu_set);
CPU_SET(2, &my_cpu_set);
CPU_SET(3, &my_cpu_set);
sched_setaffinity(my_pid, sizeof(cpu_set_t), &my_cpu_set);

Thermische Belastung

Wenn Geräte zu warm werden, können sie CPU und/oder GPU drosseln unerwartete Auswirkungen auf Spiele haben. Spiele mit komplexen Grafiken, oder eine anhaltende Netzwerkaktivität auftreten, treten eher Probleme auf.

Verwenden Sie die Thermal API, um Temperaturänderungen am Gerät zu überwachen und Maßnahmen zu ergreifen, den Stromverbrauch zu senken und die Gerätetemperatur zu senken. Wann das Gerät meldet Hitzestress, anhaltende Aktivitäten absetzen und reduzieren Stromverbrauch. Reduzieren Sie beispielsweise die Framerate oder die Polygon-Tesselierung.

Deklarieren Sie zuerst das Objekt PowerManager und und initialisieren Sie es mit der Methode onCreate(). Fügen Sie dem Objekt einen Listener für Temperaturstatus hinzu.

Kotlin

class MainActivity : AppCompatActivity() {
    lateinit var powerManager: PowerManager

    override fun onCreate(savedInstanceState: Bundle?) {
        powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
        powerManager.addThermalStatusListener(thermalListener)
    }
}

Java

public class MainActivity extends AppCompatActivity {
    PowerManager powerManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        powerManager.addThermalStatusListener(thermalListener);
    }
}

Legen Sie die Aktionen fest, die ausgeführt werden sollen, wenn der Listener einen Status erkennt. ändern können. Wenn in deinem Spiel C/C++ verwendet wird, füge Code für die Temperaturstufen in onThermalStatusChanged(), um mithilfe von JNI deinen nativen Spielcode aufzurufen oder verwenden Sie die native Thermal API.

Kotlin

val thermalListener = object : PowerManager.OnThermalStatusChangedListener() {
    override fun onThermalStatusChanged(status: Int) {
        when (status) {
            PowerManager.THERMAL_STATUS_NONE -> {
                // No thermal status, so no action necessary
            }

            PowerManager.THERMAL_STATUS_LIGHT -> {
                // Add code to handle light thermal increase
            }

            PowerManager.THERMAL_STATUS_MODERATE -> {
                // Add code to handle moderate thermal increase
            }

            PowerManager.THERMAL_STATUS_SEVERE -> {
                // Add code to handle severe thermal increase
            }

            PowerManager.THERMAL_STATUS_CRITICAL -> {
                // Add code to handle critical thermal increase
            }

            PowerManager.THERMAL_STATUS_EMERGENCY -> {
                // Add code to handle emergency thermal increase
            }

            PowerManager.THERMAL_STATUS_SHUTDOWN -> {
                // Add code to handle immediate shutdown
            }
        }
    }
}

Java

PowerManager.OnThermalStatusChangedListener thermalListener =
    new PowerManager.OnThermalStatusChangedListener () {

    @Override
    public void onThermalStatusChanged(int status) {

        switch (status)
        {
            case PowerManager.THERMAL_STATUS_NONE:
                // No thermal status, so no action necessary
                break;

            case PowerManager.THERMAL_STATUS_LIGHT:
                // Add code to handle light thermal increase
                break;

            case PowerManager.THERMAL_STATUS_MODERATE:
                // Add code to handle moderate thermal increase
                break;

            case PowerManager.THERMAL_STATUS_SEVERE:
                // Add code to handle severe thermal increase
                break;

            case PowerManager.THERMAL_STATUS_CRITICAL:
                // Add code to handle critical thermal increase
                break;

            case PowerManager.THERMAL_STATUS_EMERGENCY:
                // Add code to handle emergency thermal increase
                break;

            case PowerManager.THERMAL_STATUS_SHUTDOWN:
                // Add code to handle immediate shutdown
                break;
        }
    }
};

Touch-to-Display-Latenz

Spiele, die Frames so schnell wie möglich rendern, erzeugen ein GPU-gebundenes Szenario. sodass der Frame-Zwischenspeicher überfüllt ist. Die CPU muss auf die GPU warten, was zu einer spürbaren Verzögerung zwischen der Eingabe eines Spielers und der Eingabe Effekts auf dem Bildschirm.

Um zu ermitteln, ob Sie das Frame-Tempo Ihres Spiels verbessern könnten, führen Sie die folgenden Schritten:

  1. Erstellen Sie einen Systrace-Bericht, der die Kategorien gfx und input enthält. Diese Kategorien sind besonders hilfreich, um die die Touch-to-Display-Latenz.
  2. Sieh im Abschnitt SurfaceView eines Systrace-Berichts nach. Überfüllter Zwischenspeicher führt dazu, dass die Anzahl der ausstehenden Pufferzeichnungen zwischen 1 und 2 wechselt (siehe Abbildung). in Abbildung 5:

    Diagramm von
Pufferwarteschlange in einem System-Trace

    <ph type="x-smartling-placeholder">
    </ph> Abbildung 5: Systrace-Bericht mit einem überfüllten Zwischenspeicher, der zeitweise zu voll, um Zeichenbefehle zu akzeptieren

Führen Sie die beschriebenen Aktionen aus, um diese Inkonsistenz bei der Frametaktung zu verringern. finden Sie in den folgenden Abschnitten:

Integriere die Android Frame Pacing API in dein Spiel

Mit der Android Frame Pacing API können Sie Frame-Swaps durchführen und ein Tauschintervall so definieren, dass euer Spiel eine konsistentere Framerate zu erzielen.

Auflösung von Nicht-UI-Assets deines Spiels reduzieren

Die Displays auf modernen Mobilgeräten enthalten viel mehr Pixel als ein Player Sie können also ein Downsampling durchführen, das bei einem Durchlauf von 5 oder 10 Pixeln enthält eine Farbe. Angesichts der Struktur der meisten Display-Caches ist es am besten, Reduzieren Sie die Auflösung nur in einer Dimension.

Verringern Sie jedoch nicht die Auflösung der UI-Elemente Ihres Spiels. Es ist wichtig, um die Linienstärke dieser Elemente beizubehalten, damit eine groß genug Größe des Berührungszielbereichs für alle Ihre Spieler.

Rendering-Flüssigkeit

Wenn SurfaceFlinger einen Displaypuffer verwendet, um eine Szene in Ihrem Spiel zu zeigen, erhöht sich die CPU-Aktivität kurz. Wenn diese Spitzen in der CPU-Aktivität auftreten kann das Spiel ruckeln. Das Diagramm in Abbildung 6 zeigt den Grund dafür an:

Diagramm der Frames
VSync-Fenster fehlt, weil sie zu spät mit dem Zeichnen begonnen hat

<ph type="x-smartling-placeholder">
</ph> Abbildung 6: Systrace-Bericht, der zeigt, wie ein Frame eine Vsync übersehen kann

Wenn ein Frame zu spät beginnt, zu spät zu zeichnen, möglicherweise nur um wenige Millisekunden, nächsten Anzeigefenster öffnen. Der Frame muss dann warten, bis die nächste Vsync- angezeigt wird (33 Millisekunden bei einem Spiel mit 30 fps). eine merkliche Verzögerung aus Sicht des Spielers.

Verwenden Sie in diesem Fall die Android Frame Pacing API, die immer einen neuen Frame auf einem VSync-Wellenfront

Speicherstatus

Wenn Sie Ihr Spiel über einen längeren Zeitraum laufen lassen, aufgrund von unzureichendem Arbeitsspeicher.

Überprüfen Sie in diesem Fall die CPU-Aktivität in einem Systrace-Bericht sendet das System Aufrufe an den Daemon kswapd. Wenn viele Anrufe eingehen solltet ihr euch genauer ansehen, wie sich euer Spiel das Verwalten und Bereinigen des Arbeitsspeichers.

Weitere Informationen finden Sie im Hilfeartikel Speicher in Spielen effektiv verwalten.

Thread-Status

Bei der Navigation durch die typischen Elemente eines Systrace-Berichts können Sie folgende Informationen abrufen: Die Zeit, die ein bestimmter Thread in jedem möglichen Thread verbracht hat wie in Abbildung 7 dargestellt:

Diagramm eines
Systrace-Bericht

<ph type="x-smartling-placeholder">
</ph> Abbildung 7: Systrace-Bericht, der zeigt, wie die Auswahl eines Threads um eine Statuszusammenfassung für diesen Thread anzuzeigen.

Wie Abbildung 7 zeigt, stellen Sie möglicherweise fest, dass sich die Threads Ihres Spiels nicht im "Laufen" oder „ausführbar“ so oft wie nötig angeben. In der folgenden Liste zeigt mehrere häufige Gründe, warum ein bestimmter Thread regelmäßig in einen ungewöhnlichen Zustand übergehen:

  • Wenn ein Thread über einen längeren Zeitraum inaktiv ist, durch Sperrenkonflikte oder das Warten auf GPU-Aktivität verursacht.
  • Wenn ein Thread bei der E/A ständig blockiert wird, liest du entweder zu viele Daten. oder das Spiel ruckelt.

Weitere Informationen

Weitere Informationen dazu, wie du die Leistung deines Spiels verbessern kannst, findest du hier: Weitere Informationen:

Videos