Mit Simpleperf die Leistung eines Geräts bewerten Simpleperf ist ein natives Profiling-Tool für Apps und native Prozesse unter Android. Mit dem CPU-Profiler können Sie die CPU-Auslastung und Threadaktivität der App in Echtzeit prüfen.
Es gibt zwei für Nutzer sichtbare Leistungsindikatoren:
- Vorhersehbare, wahrnehmbare Leistung. Lässt die Benutzeroberfläche Frames aus oder wird sie konstant mit 60 FPS gerendert? Wird Audio ohne Artefakte oder Knackgeräusche wiedergegeben? Wie lang ist die Verzögerung zwischen der Berührung des Bildschirms durch den Nutzer und der Anzeige des Effekts auf dem Display?
- Zeit, die für längere Vorgänge erforderlich ist (z. B. das Öffnen von Apps).
Der erste Indikator ist auffälliger als der zweite. Nutzer bemerken in der Regel Verzögerung, 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 bemerkbar und trägt erheblich zur Wahrnehmung eines Geräts bei.
Bei einem schnellen Gerät ist die UI-Pipeline daher das Wichtigste im System, abgesehen von dem, was erforderlich ist, um die UI-Pipeline funktionsfähig zu halten. Das bedeutet, dass die UI-Pipeline alle anderen Arbeiten unterbrechen sollte, die für eine flüssige Benutzeroberfläche nicht erforderlich sind. Um eine flüssige Benutzeroberfläche zu gewährleisten, müssen die Hintergrundsynchronisierung, die Benachrichtigungsübermittlung und ähnliche Aufgaben verzögert werden, wenn UI-Aufgaben ausgeführt werden können. Es ist akzeptabel, die Leistung längerer Vorgänge (HDR+-Laufzeit, App-Start usw.) zu beeinträchtigen, um eine flüssige Benutzeroberfläche zu gewährleisten.
Kapazität im Vergleich zu Jitter
Bei der Betrachtung der Geräteleistung sind Kapazität und Jitter zwei aussagekräftige Messwerte.
Kapazität
Die Kapazität ist die Gesamtmenge einer Ressource, die das Gerät über einen bestimmten Zeitraum besitzt. Dabei kann es sich um CPU-Ressourcen, GPU-Ressourcen, E/A-Ressourcen, Netzwerkressourcen, Arbeitsspeicherbandbreite oder einen ähnlichen Messwert handeln. Bei der Untersuchung der Leistung des gesamten Systems kann es hilfreich sein, die einzelnen Komponenten zu abstrahieren und einen einzelnen Messwert anzunehmen, der die Leistung bestimmt. Dies gilt insbesondere bei der Optimierung eines neuen Geräts, da die auf diesem Gerät ausgeführten Arbeitslasten wahrscheinlich festgelegt sind.
Die Kapazität eines Systems variiert je nach den verfügbaren Rechenressourcen. Die Änderung der CPU-/GPU-Frequenz ist die wichtigste Möglichkeit, die Kapazität zu ändern. Es gibt aber auch andere Möglichkeiten, z. B. die Anzahl der verfügbaren CPU-Kerne zu ändern. Dementsprechend entspricht die Kapazität eines Systems 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 überwiegend von der ausgeführten App bestimmt. Daher kann die Plattform wenig tun, um die für eine bestimmte Arbeitslast erforderliche Kapazität anzupassen. Die Möglichkeiten dazu beschränken sich auf Laufzeitverbesserungen (Android-Framework, ART, Bionic, GPU-Compiler/Treiber, Kernel).
Jitter
Die erforderliche Kapazität für eine Arbeitslast ist leicht zu erkennen, Jitter ist jedoch ein ungenaueres 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. Darin wird untersucht, warum der Supercomputer ASCI Q nicht die erwartete Leistung erbracht hat. Der Artikel ist 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 Aufgaben ausgeführt werden. Oft handelt es sich um Aufgaben, die ausgeführt werden müssen, aber möglicherweise keine strengen Zeitvorgaben haben, die dazu führen, dass sie zu einem bestimmten Zeitpunkt ausgeführt werden. Da es sich um ein zufälliges Verhalten handelt, ist es äußerst schwierig, die Existenz von Jitter für eine bestimmte Arbeitslast zu widerlegen. Es ist auch äußerst schwierig zu beweisen, dass eine bekannte Jitter-Quelle die Ursache eines bestimmten Leistungsproblems war. Die am häufigsten verwendeten Tools zur Diagnose von Jitter-Ursachen (z. B. Tracing oder Logging) können selbst Jitter verursachen.
Zu den Jitter-Quellen, die in realen Android-Implementierungen auftreten, gehören:
- Planerverzögerung
- Interrupt-Handler
- Treibercode, der zu lange ausgeführt wird, wobei Preemption oder Interrupts deaktiviert sind
- Lang andauernde Softirqs
- Sperrenkonflikt (App, Framework, Kernel-Treiber, Binder-Sperre, Mmap-Sperre)
- Konflikt bei Dateideskriptoren, bei dem ein Thread mit niedriger Priorität die Sperre für eine Datei hält und verhindert, dass ein Thread mit hoher Priorität ausgeführt wird
- Ausführen von UI-kritischem Code in Workqueues, wo er verzögert werden könnte
- CPU-Leerlaufübergänge
- Logging
- E/A-Verzögerungen
- Unnötige Prozesserstellung (z. B.
CONNECTIVITY_CHANGE-Broadcasts) - Page-Cache-Thrashing aufgrund von unzureichendem kostenlosem Arbeitsspeicher
Die für einen bestimmten Zeitraum mit Jitter erforderliche Zeit kann mit zunehmender Kapazität abnehmen oder nicht. Wenn ein Treiber beispielsweise Interrupts deaktiviert lässt, während er auf eine Lesung über einen i2c-Bus wartet, dauert es unabhängig davon, ob die CPU mit 384 MHz oder 2 GHz läuft, eine bestimmte Zeit. Eine Erhöhung der Kapazität ist keine praktikable Lösung zur Verbesserung der Leistung, wenn Jitter auftritt. Schnellere Prozessoren verbessern die Leistung in Situationen mit Jitter-Beschränkungen in der Regel nicht.
Im Gegensatz zur Kapazität liegt Jitter fast ausschließlich im Verantwortungsbereich des Systemanbieters.
Arbeitsspeichernutzung
Die Arbeitsspeichernutzung wird traditionell für eine schlechte Leistung verantwortlich gemacht. Die Nutzung selbst ist zwar kein Leistungsproblem, kann aber durch Lowmemorykiller-Overhead, Dienstneustarts und Page-Cache-Thrashing zu Jitter führen. Durch die Reduzierung der Arbeitsspeichernutzung können die direkten Ursachen für eine schlechte Leistung vermieden werden. Es gibt aber möglicherweise auch andere gezielte Verbesserungen, die diese Ursachen vermeiden (z. B. das Fixieren des Frameworks, um zu verhindern, dass es ausgelagert wird, wenn es kurz darauf wieder ausgelagert wird).
Anfängliche Geräteleistung analysieren
Ein funktionsfähiges, aber schlecht funktionierendes System zu verwenden und zu versuchen, das Verhalten des Systems zu korrigieren, indem man einzelne Fälle von für Nutzer sichtbarer schlechter Leistung betrachtet, ist keine gute Strategie. Da eine schlechte Leistung in der Regel nicht leicht reproduzierbar ist (d. h. Jitter) oder ein App-Problem vorliegt, verhindern zu viele Variablen im Gesamtsystem, dass diese Strategie effektiv ist. Daher ist es sehr einfach, Ursachen falsch zu identifizieren und nur geringfügige Verbesserungen vorzunehmen, während systemische Möglichkeiten zur Verbesserung der Leistung im gesamten System nicht genutzt werden.
Verwenden Sie stattdessen den folgenden allgemeinen Ansatz, wenn Sie ein neues Gerät einrichten:
- Starten Sie das System bis zur Benutzeroberfläche, wobei alle Treiber ausgeführt werden und einige grundlegende Einstellungen für den Frequenzregler festgelegt sind. Wenn Sie die Einstellungen für den Frequenzregler ändern, wiederholen Sie alle Schritte unten.
- Prüfen Sie, ob der Kernel den Tracepoint
sched_blocked_reasonsowie andere Tracepoints in der Display-Pipeline unterstützt, die angeben, wann der Frame an das Display geliefert wird. - Erstellen Sie lange Traces der gesamten UI-Pipeline (vom Empfang der Eingabe über einen IRQ bis zum endgültigen Scanout), während Sie eine leichte und konsistente Arbeitslast ausführen (z. B. UiBench oder den Balltest in TouchLatency).
- Beheben Sie die Frame-Drops, die bei der leichten und konsistenten Arbeitslast erkannt wurden.
- Wiederholen Sie die Schritte 3 und 4, bis Sie 20 Sekunden oder länger ohne Frame-Drops ausführen können.
- Gehen Sie zu anderen für Nutzer sichtbaren Jitter-Quellen über.
Weitere einfache Maßnahmen, die Sie frühzeitig bei der Geräteeinrichtung ergreifen können:
- Prüfen Sie, ob Ihr Kernel den Patch für den Tracepoint „sched_blocked_reason“ enthält. Dieser Tracepoint wird mit der Trace-Kategorie „sched“ in systrace aktiviert und gibt die Funktion an, die für den Ruhezustand verantwortlich ist, wenn dieser Thread in den nicht unterbrechbaren Ruhezustand wechselt. Er ist für die Leistungsanalyse von entscheidender Bedeutung, da der nicht unterbrechbare Ruhezustand ein sehr häufiger Indikator für Jitter ist.
- Prüfen Sie, ob Sie ausreichend Tracing für die GPU- und Display-Pipelines haben. Bei aktuellen Qualcomm-SOCs werden Tracepoints mit folgendem Befehl 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, sodass Sie im Trace im Abschnitt mdss_fb0 zusätzliche Informationen zur Display-Pipeline (MDSS) sehen können. Bei Qualcomm-SOCs werden in der Standardansicht von systrace keine zusätzlichen
Informationen zur GPU angezeigt, die Ergebnisse sind aber im Trace selbst vorhanden (weitere Informationen finden Sie unter
Systrace
verstehen).
Bei dieser Art von Display-Tracing benötigen Sie ein einzelnes Ereignis, das direkt angibt, dass ein Frame an das Display geliefert wurde. So können Sie feststellen, ob Sie Ihre Frame Time erfolgreich erreicht haben.Wenn Ereignis Xn weniger als 16,7 ms nach Ereignis Xn-1 auftritt (bei einem 60-Hz-Display), wissen Sie, dass keine Verzögerung aufgetreten ist. Wenn Ihr SOC solche Signale nicht liefert, wenden Sie sich an Ihren Anbieter. Ohne ein eindeutiges Signal für den Abschluss eines Frames ist das Debuggen von Jitter äußerst schwierig.
Synthetische Benchmarks verwenden
Synthetische Benchmarks sind nützlich, um sicherzustellen, dass die grundlegende Funktionalität eines Geräts vorhanden ist. Es ist jedoch nicht sinnvoll, Benchmarks als Proxy für die wahrgenommene Geräteleistung zu verwenden.
Basierend auf Erfahrungen mit SOCs korrelieren Unterschiede in der Leistung bei synthetischen Benchmarks zwischen SOCs nicht mit einem ähnlichen Unterschied in der wahrnehmbaren UI-Leistung (Anzahl der Frame-Drops, 99. Perzentil der Frame-Zeit usw.). Synthetische Benchmarks sind Benchmarks, die nur die Kapazität berücksichtigen. Jitter wirkt sich nur auf die gemessene Leistung dieser Benchmarks aus, indem er Zeit von der Bulk-Operation des Benchmarks stiehlt. Daher sind synthetische Benchmark-Ergebnisse als Messwert für die vom Nutzer wahrgenommene Leistung weitgehend irrelevant.
Betrachten Sie zwei SOCs, auf denen Benchmark X ausgeführt wird. Benchmark X rendert 1.000 UI-Frames und gibt die gesamte Renderingzeit an (ein niedrigerer Wert ist besser).
- SOC 1 rendert jeden Frame von Benchmark X in 10 ms und erreicht einen Wert von 10.000.
- SOC 2 rendert 99% der Frames in 1 ms,aber 1% der Frames in 100 ms und erreicht einen Wert von 19.900, was ein deutlich besserer Wert ist.
Wenn der Benchmark ein Hinweis auf die tatsächliche UI-Leistung ist, wäre SOC 2 unbrauchbar. Bei einer Aktualisierungsrate von 60 Hz hätte SOC 2 alle 1, 5 Sekunden einen Frame mit Jitter. SOC 1 (der laut Benchmark X langsamere SOC) wäre dagegen vollkommen flüssig.
Fehlerberichte verwenden
Fehlerberichte sind manchmal für die Leistungsanalyse nützlich, aber da sie so umfangreich sind, sind sie selten hilfreich beim Debugging von sporadischen Verzögerungsproblemen. Sie können einige Hinweise darauf geben, was das System zu einem bestimmten Zeitpunkt getan hat, insbesondere wenn der Jitter im Zusammenhang mit einem App-Übergang aufgetreten ist (was in einem Fehlerbericht protokolliert wird). Fehlerberichte können auch darauf hinweisen, wenn es ein allgemeineres Problem mit dem System gibt, das seine effektive Kapazität verringern könnte (z. B. thermische Drosselung oder Arbeitsspeicherfragmentierung).
TouchLatency verwenden
Mehrere Beispiele für unerwünschtes Verhalten stammen von TouchLatency, der bevorzugten periodischen Arbeitslast für das Pixel und das Pixel XL. Sie ist unter frameworks/base/tests/TouchLatency verfügbar und hat zwei Modi: Touch-Latenz und Bouncing Ball. Um den Modus zu wechseln, klicken Sie oben rechts auf die Schaltfläche.
Der Bouncing Ball-Test ist genau so einfach, wie er aussieht: Ein Ball springt unabhängig von der Nutzereingabe unendlich lange auf dem Bildschirm herum. Er ist in der Regel auch mit Abstand der schwierigste Test, der perfekt ausgeführt werden kann. Je näher er an der Ausführung ohne Frame-Drops kommt, desto besser ist Ihr Gerät. Der Bouncing Ball-Test ist schwierig, weil er eine triviale, aber perfekt konsistente Arbeitslast ist, die mit einer sehr niedrigen Taktfrequenz ausgeführt wird. Dabei wird davon ausgegangen, dass das Gerät einen Frequenzregler hat. Wenn das Gerät stattdessen mit festen Taktfrequenzen läuft, reduzieren Sie die Taktfrequenz der CPU/GPU auf fast das Minimum, wenn Sie den Bouncing Ball-Test zum ersten Mal ausführen. Wenn das System in den Ruhezustand wechselt und die Taktfrequenzen sich dem Leerlauf nähern, steigt die erforderliche CPU-/GPU-Zeit pro Frame. Sie können den Ball beobachten und sehen, wie es ruckelt, und Sie können auch Frame-Drops in systrace sehen.
Da die Arbeitslast so konsistent ist, können Sie die meisten Jitter-Quellen viel einfacher identifizieren als bei den meisten für Nutzer sichtbaren Arbeitslasten. Dazu verfolgen Sie, was genau auf dem System ausgeführt wird, anstatt die UI-Pipeline zu beobachten. Die niedrigeren Taktfrequenzen verstärken die Auswirkungen von Jitter, da es wahrscheinlicher ist, dass Jitter zu einem Frame-Drop führt. Je näher TouchLatency an 60 FPS liegt, desto geringer ist die Wahrscheinlichkeit, dass es zu schlechtem Systemverhalten kommt, das sporadische, schwer reproduzierbare Verzögerung in größeren Apps verursacht.
Da Jitter oft (aber nicht immer) unabhängig von der Taktfrequenz ist, verwenden Sie einen Test, der mit sehr niedrigen Taktfrequenzen ausgeführt wird, um Jitter zu diagnostizieren. Die Gründe dafür sind:
- Nicht alle Jitter-Quellen sind unabhängig von der Taktfrequenz. Viele Quellen verbrauchen nur CPU-Zeit.
- Der Regler sollte die durchschnittliche Frame-Zeit durch Reduzierung der Taktfrequenz nahe an die Deadline bringen. Daher kann die Zeit, die für nicht UI-bezogene Aufgaben aufgewendet wird, dazu führen, dass ein Frame ausgelassen wird.