Leistung auswerten

Verwenden Sie Simpleperf, um die Leistung eines Geräts zu bewerten. Simpleperf ist ein natives Profiler-Tool für Apps und native Prozesse unter Android. Mit dem CPU-Profiler können Sie die CPU-Nutzung und Threadaktivität der App in Echtzeit prüfen.

Es gibt zwei für Nutzer sichtbare Leistungskennzahlen:

  • Vorhersagbare, wahrnehmbare Leistung Verliert die Benutzeroberfläche Frames oder wird sie konstant mit 60 fps gerendert? Wird der Audioinhalt ohne Artefakte oder Knacken wiedergegeben? Wie lang ist die Verzögerung zwischen dem Berühren des Displays durch den Nutzer und der Anzeige des Effekts auf dem Display?
  • Dauer längerer Vorgänge (z. B. Öffnen von Apps).

Ersteres ist deutlicher als zweiteres. Nutzer bemerken in der Regel Ruckler, können aber nicht zwischen einer App-Startzeit von 500 ms und 600 ms unterscheiden, es sei denn, sie sehen sich zwei Geräte nebeneinander an. Die Touch-Latenz ist sofort spürbar und trägt wesentlich zur Wahrnehmung eines Geräts bei.

Daher ist die UI-Pipeline auf einem schnellen Gerät das Wichtigste im System, abgesehen von den Elementen, die für die Funktion der UI-Pipeline erforderlich sind. Das bedeutet, dass die UI-Pipeline alle anderen Arbeiten vorwegnehmen sollte, die für eine flüssige Benutzeroberfläche nicht erforderlich sind. Um eine flüssige Benutzeroberfläche zu gewährleisten, müssen Hintergrundsynchronisierung, Benachrichtigungsübermittlung und ähnliche Aufgaben verzögert werden, wenn UI-Arbeiten ausgeführt werden können. Es ist akzeptabel, die Leistung längerer Vorgänge (HDR+-Laufzeit, App-Start usw.) einzubüßen, um eine flüssige Benutzeroberfläche zu erhalten.

Kapazität im Vergleich zu Jitter

Bei der Geräteleistung sind zwei wichtige Messwerte die Kapazität und der Jitter.

Kapazität

Die Kapazität ist die Gesamtmenge einer Ressource, die dem Gerät über einen bestimmten Zeitraum zur Verfügung steht. Das können CPU-Ressourcen, GPU-Ressourcen, E/A-Ressourcen, Netzwerkressourcen, Speicherbandbreite oder ähnliche Messwerte sein. Bei der Prüfung der Leistung des gesamten Systems kann es hilfreich sein, die einzelnen Komponenten zu abstrahieren und einen einzigen Messwert anzunehmen, der die Leistung bestimmt. Dies gilt insbesondere bei der Optimierung eines neuen Geräts, da die Arbeitslasten auf diesem Gerät wahrscheinlich festgelegt sind.

Die Kapazität eines Systems variiert je nach den online verfügbaren Rechenressourcen. Die Änderung der CPU-/GPU-Taktfrequenz ist das primäre Mittel zur Änderung der Kapazität. Es gibt aber auch andere Möglichkeiten, z. B. die Anzahl der online verfügbaren CPU-Kerne zu ändern. Die Kapazität eines Systems entspricht dem Stromverbrauch. Eine Änderung der Kapazität führt immer zu einer ähnlichen Änderung des Stromverbrauchs.

Die zu einem bestimmten Zeitpunkt erforderliche Kapazität wird in erster Linie von der laufenden App bestimmt. Daher kann die Plattform die für eine bestimmte Arbeitslast erforderliche Kapazität nur wenig anpassen. Die Möglichkeiten dazu sind auf Laufzeitverbesserungen beschränkt (Android-Framework, ART, Bionic, GPU-Compiler/-Treiber, Kernel).

Jitter

Während die erforderliche Kapazität für eine Arbeitslast leicht zu erkennen ist, ist Jitter ein etwas unklareres Konzept. Eine gute Einführung in Jitter als Hindernis für schnelle Systeme finden Sie im Artikel The Case of the Missing Supercomputer Performance: Achieving Optimal Performance on the 8,192 processors of ASCI Q. (Es ist eine Untersuchung, warum der ASCI Q-Supercomputer nicht die erwartete Leistung erreichte, und eine gute Einführung in die Optimierung großer Systeme.)

Auf dieser Seite wird der Begriff „Jitter“ verwendet, um das zu beschreiben, was im ASCI Q-Artikel als Rauschen bezeichnet wird. Jitter ist das zufällige Systemverhalten, das verhindert, dass wahrnehmbare Arbeit ausgeführt wird. Häufig sind dies Aufgaben, die ausgeführt werden müssen, aber möglicherweise keine strengen Zeitanforderungen haben, die dazu führen, dass sie zu einer bestimmten Zeit ausgeführt werden. Da es sich um einen Zufallseffekt handelt, ist es äußerst schwierig, das Vorhandensein von Jitter für eine bestimmte Arbeitslast zu widerlegen. Außerdem ist es extrem schwierig nachzuweisen, dass eine bekannte Jitterquelle die Ursache für ein bestimmtes Leistungsproblem war. Die Tools, die am häufigsten zur Diagnose von Jitter verwendet werden (z. B. Tracing oder Logging), können selbst Jitter verursachen.

Zu den Ursachen für Jitter bei der praktischen Implementierung von Android gehören:

  • Verzögerung des Schedulers
  • Unterbrechungs-Handler
  • Treibercode läuft zu lange, wenn Voremption oder Unterbrechungen deaktiviert sind
  • Lang andauernde Softirqs
  • Sperrenkonflikt (App, Framework, Kerneltreiber, Bindersperre, mmap-Sperre)
  • Dateideskriptorkonflikt, bei dem ein Thread mit niedriger Priorität die Sperre für eine Datei hält und so verhindert, dass ein Thread mit hoher Priorität ausgeführt wird
  • Ausführung von Code, der für die Benutzeroberfläche wichtig ist, in Arbeitswarteschlangen, wo es zu Verzögerungen kommen kann
  • CPU-Leerlaufübergänge
  • Protokollierung
  • E/A-Verzögerungen
  • Unnötige Prozesserstellung (z. B. CONNECTIVITY_CHANGE-Broadcasts)
  • Auslastung des Seitencaches aufgrund unzureichender kostenloser Arbeitsspeicher

Die erforderliche Zeit für einen bestimmten Jitterzeitraum kann mit zunehmender Kapazität sinken oder steigen. Wenn ein Treiber beispielsweise Interrupts deaktiviert lässt, während er auf ein Lesen über einen I2C-Bus wartet, dauert es unabhängig davon, ob die CPU 384 MHz oder 2 GHz hat, eine bestimmte Zeit. Eine Erhöhung der Kapazität ist keine praktikable Lösung, um die Leistung bei Jitter zu verbessern. Daher verbessern schnellere Prozessoren in der Regel nicht die Leistung bei hoher Jitter-Belastung.

Im Gegensatz zur Kapazität liegt der Jitter fast vollständig in der Zuständigkeit des Systemanbieters.

Arbeitsspeicherverbrauch

Der Speicherverbrauch wird traditionell für eine schlechte Leistung verantwortlich gemacht. Der Verbrauch selbst ist zwar kein Leistungsproblem, kann aber zu Rucklern durch den Overhead des LowMemoryKiller, Dienstneustarts und Page-Cache-Trashing führen. Durch die Verringerung der Speichernutzung können die direkten Ursachen für eine schlechte Leistung vermieden werden. Es kann jedoch auch andere gezielte Verbesserungen geben, die diese Ursachen ebenfalls vermeiden. Beispielsweise kann das Framework angepinnt werden, um zu verhindern, dass es ausgelagert wird, wenn es kurz darauf wieder eingefügt wird.

Erste Geräteleistung analysieren

Es ist keine gute Strategie, von einem funktionsfähigen, aber schlecht funktionierenden System auszugehen und zu versuchen, das Systemverhalten zu korrigieren, indem man sich einzelne Fälle von für Nutzer sichtbarer schlechter Leistung ansieht. Da eine schlechte Leistung in der Regel nicht leicht reproduzierbar ist (z. B. Ruckeln) oder ein App-Problem vorliegt, verhindern zu viele Variablen im gesamten System, dass diese Strategie effektiv ist. Daher ist es sehr einfach, Ursachen falsch zu identifizieren und kleinere Verbesserungen vorzunehmen, während systemische Möglichkeiten zur Leistungsoptimierung im gesamten System übersehen werden.

Verwenden Sie stattdessen den folgenden allgemeinen Ansatz, wenn Sie ein neues Gerät einrichten:

  1. Starten Sie das System mit allen laufenden Treibern und einigen grundlegenden Einstellungen für den Taktregler. Wenn Sie die Einstellungen für den Taktregler ändern, wiederholen Sie alle unten aufgeführten Schritte.
  2. Der Kernel muss den sched_blocked_reason-Tracepoint sowie andere Tracepoints in der Displaypipeline unterstützen, die angeben, wann der Frame an das Display gesendet wird.
  3. Erstellen Sie lange Traces der gesamten UI-Pipeline (vom Empfangen der Eingabe über einen IRQ bis zum abschließenden Scanout), während Sie eine einfache und konsistente Arbeitslast ausführen (z. B. UiBench oder den Balltest in TouchLatency).
  4. Beheben Sie die Frame-Drops, die bei der leichten und gleichmäßigen Arbeitslast erkannt wurden.
  5. Wiederhole die Schritte 3 und 4, bis du mindestens 20 Sekunden lang ohne Frame-Ausfälle laufen kannst.
  6. Fahren Sie mit anderen für Nutzer sichtbaren Ursachen von Ruckeln fort.

Zu den weiteren einfachen Dingen, die Sie zu Beginn der Geräteeinrichtung tun können, gehören:

  • Ihr Kernel muss den Tracepoint-Patch „sched_blocked_reason“ enthalten. Dieser Tracepoint wird mit der Trace-Kategorie „sched“ in systrace aktiviert und stellt die Funktion bereit, die für den Ruhemodus verantwortlich ist, wenn dieser Thread in den ununterbrechbaren Ruhemodus wechselt. Sie ist für die Leistungsanalyse entscheidend, da ein unterbrechungsfreier Ruhezustand ein sehr häufiger Indikator für Jitter ist.
  • Achten Sie darauf, dass Sie für die GPU- und Displaypipelines ausreichende Tracing-Daten haben. Auf aktuellen Qualcomm-SOCs werden Tracepoints mithilfe der folgenden Methoden aktiviert:
  • adb shell "echo 1 > /d/tracing/events/kgsl/enable"
    adb shell "echo 1 > /d/tracing/events/mdss/enable"
    

    Diese Ereignisse bleiben aktiviert, wenn Sie systrace ausführen. So können Sie im Abschnitt mdss_fb0 zusätzliche Informationen zur Displaypipeline (MDSS) im Trace sehen. Auf Qualcomm-SOCs werden in der Standardansicht von systrace keine zusätzlichen Informationen zur GPU angezeigt. Die Ergebnisse sind jedoch im Trace selbst enthalten. Weitere Informationen finden Sie unter systrace.

    Bei dieser Art der Display-Analyse benötigen Sie ein einzelnes Ereignis, das direkt angibt, dass ein Frame an das Display gesendet wurde. So können Sie feststellen, ob Sie die Framezeit erreicht haben.Wenn Ereignis Xn weniger als 16,7 ms nach Ereignis Xn-1 eintritt (vorausgesetzt, es wird ein 60-Hz-Display verwendet), ist das Ruckeln nicht zu sehen. Wenn Ihr SOC keine solchen Signale bereitstellt, bitten Sie Ihren Anbieter, sie zu erhalten. Ohne ein eindeutiges Signal für den Frame-Abschluss ist es extrem schwierig, Jitter zu beheben.

Synthetische Benchmarks verwenden

Synthetische Benchmarks sind nützlich, um sicherzustellen, dass die grundlegenden Funktionen eines Geräts vorhanden sind. Es ist jedoch nicht sinnvoll, Benchmarks als Stellvertreter für die wahrgenommene Geräteleistung zu verwenden.

Basierend auf Erfahrungen mit SOCs sind Unterschiede in der synthetischen Benchmarkleistung zwischen SOCs nicht mit einer ähnlichen Differenz bei der wahrnehmbaren UI-Leistung korreliert (z. B. Anzahl der fehlenden Frames, Framezeit des 99. Perzentil usw.). Synthetische Benchmarks sind nur Kapazitäts-Benchmarks. Jitter wirkt sich nur auf die gemessene Leistung dieser Benchmarks aus, indem er die Zeit für den Bulk-Betrieb des Benchmarks verkürzt. Daher sind synthetische Benchmark-Werte als Messwert für die von Nutzern wahrgenommene Leistung weitgehend irrelevant.

Angenommen, zwei SOCs führen Benchmark X aus, der 1.000 UI-Frames rendert und die Gesamt-Renderingzeit meldet (je niedriger, desto besser).

  • SOC 1 rendert jeden Frame von Benchmark X in 10 ms und erzielt eine Punktzahl von 10.000.
  • SOC 2 rendert 99% der Frames in 1 ms, aber 1% der Frames in 100 ms und erzielt eine deutlich bessere Punktzahl von 19.900.

Wenn der Benchmark ein Indikator für die tatsächliche Leistung der Benutzeroberfläche ist, wäre SOC 2 nicht verwendbar. Bei einer Aktualisierungsrate von 60 Hz würde SOC 2 alle 1, 5 Sekunden einen ruckeligen Frame haben. SOC 1 (das langsamere SOC gemäß Benchmark X) würde dagegen flüssig laufen.

Fehlerberichte verwenden

Fehlerberichte sind manchmal nützlich für die Leistungsanalyse, aber da sie so umfangreich sind, eignen sie sich selten für die Behebung sporadischer Ruckler. Sie können Hinweise darauf geben, was das System zu einem bestimmten Zeitpunkt getan hat, insbesondere wenn das Ruckeln bei einem App-Übergang aufgetreten ist (was in einem Fehlerbericht protokolliert wird). Fehlerberichte können auch darauf hinweisen, dass etwas grundsätzlich mit dem System nicht stimmt, was die effektive Kapazität beeinträchtigen könnte (z. B. thermische Drosselung oder Speicherfragmentierung).

TouchLatency verwenden

Mehrere Beispiele für Fehlverhalten stammen von TouchLatency, der bevorzugten periodischen Arbeitslast für Google Pixel und Google Pixel XL. Sie ist unter frameworks/base/tests/TouchLatency verfügbar und hat zwei Modi: Touchlatenz und springender Ball. Um den Modus zu wechseln, klicken Sie auf die Schaltfläche oben rechts.

Der Test mit dem springenden Ball ist genau so einfach, wie er aussieht: Ein Ball springt unabhängig von der Nutzereingabe ewig auf dem Bildschirm herum. In der Regel ist es auch bei weitem der schwierigste Test, der perfekt ausgeführt werden muss. Je näher das Gerät an einer Ausführung ohne fehlende Frames kommt, desto besser ist es. Der Sprungballtest ist schwierig, da es sich um eine triviale, aber absolut konsistente Arbeitslast handelt, die mit einer sehr niedrigen Taktfrequenz ausgeführt wird. Dabei wird davon ausgegangen, dass das Gerät einen Taktregler hat. Wenn das Gerät stattdessen mit festen Taktfrequenzen ausgeführt wird, senken Sie die CPU/GPU beim ersten Ausführen des Sprungballtests auf nahezu das Minimum. Wenn das System in den Ruhemodus wechselt und die Taktfrequenzen näher am Leerlauf liegen, erhöht sich die erforderliche CPU-/GPU-Zeit pro Frame. Sie können sich den Ball ansehen und sehen, dass es ruckelt. Außerdem sehen Sie in systrace fehlende Frames.

Da die Arbeitslast so konsistent ist, können Sie die meisten Jitterquellen viel einfacher identifizieren als bei den meisten für Nutzer sichtbaren Arbeitslasten. Dazu erfassen Sie, was genau während jedes verpassten Frames auf dem System ausgeführt wird, anstatt die UI-Pipeline zu beobachten. Die niedrigeren Takte verstärken die Auswirkungen von Jitter, da die Wahrscheinlichkeit höher ist, dass Jitter zu einem Frame-Drop führt. Je näher die TouchLatency an 60 FPS liegt, desto unwahrscheinlicher ist es, dass es zu Systemfehlern kommt, die in größeren Apps zu sporadisch auftretenden, schwer reproduzierbaren Rucklern führen.

Da Jitter oft (aber nicht immer) unabhängig von der Taktfrequenz ist, sollten Sie zur Diagnose von Jitter einen Test verwenden, der mit sehr niedrigen Taktfrequenzen ausgeführt wird. Das hat folgende Gründe:

  • Nicht jeder Jitter ist unabhängig von der Taktgeschwindigkeit. Viele Quellen verbrauchen nur CPU-Zeit.
  • Der Governor sollte die durchschnittliche Framezeit durch Heruntertakten an den Grenzwert heranführen, damit die Zeit für die Ausführung von Aufgaben ohne Benutzeroberfläche nicht zu hoch wird und ein Frame verworfen wird.