Identifizieren von Jitter-bedingtem Jank

Jitter ist das zufällige Systemverhalten, das die Ausführung wahrnehmbarer Arbeit verhindert. Auf dieser Seite wird beschrieben, wie Sie Jitter-bezogene Jank-Probleme identifizieren und beheben können.

Verzögerung des Anwendungs-Thread-Schedulers

Die Scheduler-Verzögerung ist das offensichtlichste Symptom von Jitter: Ein Prozess, der ausgeführt werden sollte, wird ausführbar gemacht, wird jedoch für längere Zeit nicht ausgeführt. Die Bedeutung der Verzögerung variiert je nach Kontext. Zum Beispiel:

  • Ein zufälliger Hilfsthread in einer App kann wahrscheinlich ohne Probleme um viele Millisekunden verzögert werden.
  • Der UI-Thread einer Anwendung kann möglicherweise einen Jitter von 1–2 ms tolerieren.
  • Treiber-Kthreads, die als SCHED_FIFO ausgeführt werden, können Probleme verursachen, wenn sie vor der Ausführung 500 us lang lauffähig sind.

Ausführbare Zeiten können in Systrace durch den blauen Balken vor einem laufenden Segment eines Threads identifiziert werden. Eine ausführbare Zeit kann auch durch die Zeitspanne zwischen dem sched_wakeup Ereignis für einen Thread und dem sched_switch Ereignis bestimmt werden, das den Start der Thread-Ausführung signalisiert.

Threads, die zu lange laufen

Anwendungs-UI-Threads, die zu lange ausführbar sind, können Probleme verursachen. Threads auf niedrigerer Ebene mit langen Ausführungszeiten haben im Allgemeinen unterschiedliche Ursachen. Wenn Sie jedoch versuchen, die Ausführungszeit von UI-Threads gegen Null zu drücken, müssen möglicherweise einige der gleichen Probleme behoben werden, die dazu führen, dass Threads auf niedrigeren Ebenen lange Ausführungszeiten haben. Um Verzögerungen zu vermeiden:

  1. Verwenden Sie CPU-Sets wie unter „Thermische Drosselung“ beschrieben.
  2. Erhöhen Sie den CONFIG_HZ-Wert.
    • In der Vergangenheit wurde der Wert auf Arm- und Arm64-Plattformen auf 100 festgelegt. Dies ist jedoch ein Zufall der Geschichte und kein guter Wert für interaktive Geräte. CONFIG_HZ=100 bedeutet, dass ein Augenblick 10 ms lang ist, was bedeutet, dass der Lastausgleich zwischen CPUs 20 ms (zwei Augenblicke) dauern kann. Dies kann erheblich zum Ruckeln auf einem belasteten System beitragen.
    • Aktuelle Geräte (Nexus 5X, Nexus 6P, Pixel und Pixel XL) werden mit CONFIG_HZ=300 ausgeliefert. Dies sollte zu vernachlässigbaren Stromkosten führen und gleichzeitig die Laufzeiten erheblich verbessern. Wenn Sie nach der Änderung von CONFIG_HZ einen erheblichen Anstieg des Stromverbrauchs oder Leistungsprobleme feststellen, verwendet einer Ihrer Treiber wahrscheinlich einen Timer, der auf reinen Sekunden statt auf Millisekunden basiert und in Sekunden umrechnet. Dies ist normalerweise eine einfache Lösung (siehe den Patch , der kgsl-Timer-Probleme auf Nexus 5X und 6P bei der Konvertierung in CONFIG_HZ=300 behebt).
    • Schließlich haben wir mit CONFIG_HZ=1000 auf Nexus/Pixel experimentiert und festgestellt, dass es aufgrund des geringeren RCU-Overheads eine spürbare Leistungs- und Leistungsreduzierung bietet.

Allein mit diesen beiden Änderungen sollte ein Gerät hinsichtlich der Laufzeit des UI-Threads unter Last viel besser aussehen.

Verwenden von sys.use_fifo_ui

Sie können versuchen, die Ausführungszeit des UI-Threads auf Null zu reduzieren, indem Sie die Eigenschaft sys.use_fifo_ui auf 1 setzen.

Warnung : Verwenden Sie diese Option nicht für heterogene CPU-Konfigurationen, es sei denn, Sie verfügen über einen kapazitätsbewussten RT-Scheduler. Und derzeit ist KEIN RT-SCHEDULER, der derzeit für den Versand zuständig ist, über die Kapazität informiert . Wir arbeiten an einem für EAS, aber es ist noch nicht verfügbar. Der Standard-RT-Scheduler basiert ausschließlich auf RT-Prioritäten und darauf, ob eine CPU bereits über einen RT-Thread mit gleicher oder höherer Priorität verfügt.

Infolgedessen verschiebt der Standard-RT-Scheduler Ihren relativ lang laufenden UI-Thread mit minimaler Frequenz problemlos von einem großen Hochfrequenzkern auf einen kleinen Kern, wenn zufällig ein FIFO-Kthread mit höherer Priorität auf demselben großen Kern aktiviert wird. Dies führt zu erheblichen Leistungseinbußen . Da diese Option auf einem ausgelieferten Android-Gerät noch nicht verwendet wurde, wenden Sie sich, wenn Sie sie nutzen möchten, an das Android-Performance-Team, um bei der Validierung behilflich zu sein.

Wenn sys.use_fifo_ui aktiviert ist, verfolgt ActivityManager den UI-Thread und RenderThread (die beiden UI-kritischsten Threads) der obersten Anwendung und macht diese Threads zu SCHED_FIFO anstelle von SCHED_OTHER. Dadurch wird Jitter von UI und RenderThreads effektiv eliminiert; Die Spuren, die wir mit aktivierter Option gesammelt haben, zeigen ausführbare Zeiten in der Größenordnung von Mikrosekunden statt Millisekunden.

Da der RT Load Balancer jedoch nicht kapazitätsbewusst war, kam es zu einer Reduzierung der Anwendungsstartleistung um 30 %, da der für den Start der App zuständige UI-Thread von einem 2,1-GHz-Gold-Kryo-Kern auf einen 1,5-GHz-Silber-Kryo-Kern verschoben wurde . Mit einem kapazitätsbewussten RT-Load-Balancer sehen wir in vielen unserer UI-Benchmarks eine gleichwertige Leistung bei Massenvorgängen und eine Reduzierung der Frametimes im 95. und 99. Perzentil um 10–15 %.

Verkehr unterbrechen

Da ARM-Plattformen Interrupts standardmäßig nur an CPU 0 liefern, empfehlen wir die Verwendung eines IRQ-Balancers (irqbalance oder msm_irqbalance auf Qualcomm-Plattformen).

Während der Pixel-Entwicklung haben wir einen Fehler festgestellt, der direkt auf die Überlastung von CPU 0 durch Interrupts zurückzuführen ist. Wenn beispielsweise der mdss_fb0 Thread auf CPU 0 geplant war, war die Wahrscheinlichkeit eines Ruckelns aufgrund eines Interrupts, der fast unmittelbar vor dem Scanout durch die Anzeige ausgelöst wurde, viel größer. mdss_fb0 wäre mitten in seiner eigenen Arbeit mit einer sehr knappen Frist und würde dann etwas Zeit an den MDSS-Interrupt-Handler verlieren. Zunächst versuchten wir, dies zu beheben, indem wir die CPU-Affinität des mdss_fb0-Threads auf die CPUs 1–3 setzten, um Konflikte mit dem Interrupt zu vermeiden. Dann stellten wir jedoch fest, dass wir msm_irqbalance noch nicht aktiviert hatten. Bei aktiviertem msm_irqbalance wurde das Ruckeln deutlich verbessert, selbst wenn sich sowohl mdss_fb0 als auch der MDSS-Interrupt auf derselben CPU befanden, da die Konkurrenz durch andere Interrupts geringer war.

Dies kann in Systrace durch einen Blick auf den Sched-Abschnitt und den IRQ-Abschnitt identifiziert werden. Der Abschnitt „Sched“ zeigt, was geplant wurde, aber ein überlappender Bereich im Abschnitt „IRQ“ bedeutet, dass während dieser Zeit ein Interrupt statt des normalerweise geplanten Prozesses ausgeführt wird. Wenn Sie feststellen, dass während einer Unterbrechung erhebliche Zeitspannen in Anspruch genommen werden, haben Sie folgende Möglichkeiten:

  • Machen Sie den Interrupt-Handler schneller.
  • Verhindern Sie, dass es überhaupt zu einer Unterbrechung kommt.
  • Ändern Sie die Häufigkeit des Interrupts so, dass er phasenverschoben zu anderen regulären Arbeiten ist, die möglicherweise beeinträchtigt werden (sofern es sich um einen regulären Interrupt handelt).
  • Legen Sie die CPU-Affinität des Interrupts direkt fest und verhindern Sie, dass dieser ausgeglichen wird.
  • Legen Sie die CPU-Affinität des Threads fest, den der Interrupt stört, um den Interrupt zu vermeiden.
  • Verlassen Sie sich auf den Interrupt-Balancer, um den Interrupt auf eine weniger belastete CPU zu verschieben.

Das Festlegen der CPU-Affinität wird im Allgemeinen nicht empfohlen, kann aber in bestimmten Fällen nützlich sein. Im Allgemeinen ist es zu schwierig, den Zustand des Systems für die häufigsten Interrupts vorherzusagen. Wenn Sie jedoch eine sehr spezifische Reihe von Bedingungen haben, die bestimmte Interrupts auslösen, bei denen das System stärker als normal eingeschränkt ist (z. B. VR), kann dies zu einer expliziten CPU-Affinität führen eine gute Lösung sein.

Lange Softirqs

Während ein Softirq ausgeführt wird, wird die Vorkaufsberechtigung deaktiviert. Softirqs können auch an vielen Stellen im Kernel ausgelöst und innerhalb eines Benutzerprozesses ausgeführt werden. Wenn genügend Softirq-Aktivität vorhanden ist, stoppen Benutzerprozesse die Ausführung von Softirqs und ksoftirqd wird aktiviert, um Softirqs auszuführen und einen Lastausgleich durchzuführen. Normalerweise ist das in Ordnung. Allerdings kann ein einzelner, sehr langer Softirq verheerende Auswirkungen auf das System haben.


Softirqs sind im IRQ-Abschnitt einer Ablaufverfolgung sichtbar, sodass sie leicht zu erkennen sind, wenn das Problem während der Ablaufverfolgung reproduziert werden kann. Da ein Softirq innerhalb eines Benutzerprozesses ausgeführt werden kann, kann sich ein fehlerhafter Softirq auch ohne ersichtlichen Grund als zusätzliche Laufzeit innerhalb eines Benutzerprozesses manifestieren. Wenn Sie das sehen, überprüfen Sie den IRQ-Bereich, um zu sehen, ob Softirqs dafür verantwortlich sind.

Treiber lassen Vorkaufsrecht oder IRQs zu lange deaktiviert

Das Deaktivieren von Preemption oder Interrupts für einen zu langen Zeitraum (mehrere zehn Millisekunden) führt zu Störungen. Typischerweise manifestiert sich der Fehler dadurch, dass ein Thread ausführbar wird, aber nicht auf einer bestimmten CPU ausgeführt wird, selbst wenn der ausführbare Thread eine deutlich höhere Priorität (oder SCHED_FIFO) als der andere Thread hat.

Einige Richtlinien:

  • Wenn der ausführbare Thread SCHED_FIFO und der laufende Thread SCHED_OTHER ist, sind für den laufenden Thread die Vorbelegung oder Interrupts deaktiviert.
  • Wenn der ausführbare Thread eine deutlich höhere Priorität (100) als der laufende Thread (120) hat, sind für den laufenden Thread wahrscheinlich die Vorbelegung oder Interrupts deaktiviert, wenn der ausführbare Thread nicht innerhalb von zwei Sekunden ausgeführt wird.
  • Wenn der ausführbare Thread und der laufende Thread die gleiche Priorität haben, sind für den laufenden Thread wahrscheinlich die Vorbelegung oder Interrupts deaktiviert, wenn der ausführbare Thread nicht innerhalb von 20 ms ausgeführt wird.

Bedenken Sie, dass die Ausführung eines Interrupt-Handlers Sie daran hindert, andere Interrupts zu bedienen, wodurch auch die Vorbelegung deaktiviert wird.


Eine weitere Möglichkeit zur Identifizierung fehlerhafter Regionen ist der preemptirqsoff-Tracer (siehe Verwenden von dynamischem Ftrace ). Dieser Tracer kann einen viel besseren Einblick in die Grundursache einer unterbrechungsfreien Region (z. B. Funktionsnamen) geben, erfordert jedoch einen aufwändigeren Aufwand, um ihn zu aktivieren. Auch wenn es sich eher auf die Leistung auswirkt, ist es auf jeden Fall einen Versuch wert.

Falsche Verwendung von Arbeitswarteschlangen

Interrupt-Handler müssen häufig Arbeiten ausführen, die außerhalb eines Interrupt-Kontexts ausgeführt werden können, sodass die Arbeit auf verschiedene Threads im Kernel verteilt werden kann. Einem Treiberentwickler fällt möglicherweise auf, dass der Kernel über eine sehr praktische systemweite asynchrone Aufgabenfunktion namens Workqueues verfügt, und er könnte diese für Interrupt-bezogene Arbeiten verwenden.

Allerdings sind Arbeitswarteschlangen fast immer die falsche Lösung für dieses Problem, da sie immer SCHED_OTHER sind. Viele Hardware-Interrupts befinden sich im kritischen Leistungspfad und müssen sofort ausgeführt werden. Für Arbeitswarteschlangen gibt es keine Garantie, wann sie ausgeführt werden. Jedes Mal, wenn wir eine Arbeitswarteschlange im kritischen Leistungspfad sahen, kam es unabhängig vom Gerät zu sporadischen Störungen. Bei Pixel mit einem Flaggschiff-Prozessor haben wir festgestellt, dass eine einzelne Arbeitswarteschlange je nach Scheduler-Verhalten und anderen auf dem System ausgeführten Dingen um bis zu 7 ms verzögert werden konnte, wenn das Gerät unter Last stand.

Anstelle einer Arbeitswarteschlange sollten Treiber, die unterbrechungsähnliche Arbeiten in einem separaten Thread verarbeiten müssen, ihren eigenen SCHED_FIFO-Kthread erstellen. Hilfe dazu mit kthread_work-Funktionen finden Sie in diesem Patch .

Konflikt um Framework-Sperre

Ein Framework-Sperrkonflikt kann eine Ursache für Ruckeln oder andere Leistungsprobleme sein. Es wird normalerweise durch die ActivityManagerService-Sperre verursacht, kann aber auch bei anderen Sperren auftreten. Beispielsweise kann sich die PowerManagerService-Sperre auf die Leistung des Bildschirms auswirken. Wenn Sie dies auf Ihrem Gerät sehen, gibt es keine gute Lösung, da es nur durch architektonische Verbesserungen des Frameworks verbessert werden kann. Wenn Sie jedoch Code ändern, der innerhalb von system_server ausgeführt wird, ist es wichtig, Sperren, insbesondere die ActivityManagerService-Sperre, nicht über einen längeren Zeitraum aufrechtzuerhalten.

Konflikt um Bindersperre

Historisch gesehen verfügte Binder über eine einzige globale Sperre. Wenn der Thread, der eine Binder-Transaktion ausführt, während der Sperre vorzeitig freigegeben wurde, kann kein anderer Thread eine Binder-Transaktion ausführen, bis der ursprüngliche Thread die Sperre aufgehoben hat. Das ist schlecht; Binder-Konflikte können alles im System blockieren, einschließlich des Sendens von UI-Updates an die Anzeige (UI-Threads kommunizieren mit SurfaceFlinger über Binder).

Android 6.0 enthielt mehrere Patches, um dieses Verhalten zu verbessern, indem die Vorbelegung deaktiviert wurde, während die Ordnersperre beibehalten wurde. Dies war nur deshalb sicher, weil die Ordnersperre einige Mikrosekunden der tatsächlichen Laufzeit gehalten werden sollte. Dies verbesserte die Leistung in unbestrittenen Situationen erheblich und verhinderte Konflikte, indem die meisten Planerwechsel verhindert wurden, während die Ordnersperre gehalten wurde. Allerdings konnte die Vorkaufsberechtigung nicht während der gesamten Laufzeit der Bindersperre deaktiviert werden, was bedeutet, dass die Vorkaufssperre für Funktionen aktiviert wurde, die ruhen konnten (z. B. copy_from_user), was die gleiche Vorkaufssperre wie im ursprünglichen Fall bewirken konnte. Als wir die Patches nach oben schickten, teilten sie uns sofort mit, dass dies die schlechteste Idee der Geschichte sei. (Wir stimmten ihnen zu, konnten aber auch nicht mit der Wirksamkeit der Patches bei der Verhinderung von Ruckeln streiten.)

fd-Konflikt innerhalb eines Prozesses

Das ist selten. Ihr Ruckeln ist wahrscheinlich nicht dadurch verursacht.

Wenn Sie jedoch mehrere Threads innerhalb eines Prozesses haben, die denselben FD schreiben, ist es möglich, einen Konflikt auf diesem FD zu sehen. Das einzige Mal, dass wir dies jedoch während des Pixel-Starts sahen, war während eines Tests, bei dem Threads mit niedriger Priorität versuchten, die gesamte CPU zu belegen Zeit, während ein einzelner Thread mit hoher Priorität innerhalb desselben Prozesses ausgeführt wurde. Alle Threads schrieben auf den Ablaufverfolgungsmarker fd und der Thread mit hoher Priorität konnte auf dem Ablaufverfolgungsmarker fd blockiert werden, wenn ein Thread mit niedriger Priorität die fd-Sperre hielt und dann vorbelegt wurde. Wenn die Ablaufverfolgung für Threads mit niedriger Priorität deaktiviert war, gab es kein Leistungsproblem.

Wir konnten dies in keiner anderen Situation reproduzieren, aber es lohnt sich, darauf hinzuweisen, dass es eine mögliche Ursache für Leistungsprobleme beim Tracing darstellt.

Unnötige CPU-Leerlaufübergänge

Beim Umgang mit IPC, insbesondere mit Multiprozess-Pipelines, kommt es häufig zu Abweichungen im folgenden Laufzeitverhalten:

  1. Thread A läuft auf CPU 1.
  2. Thread A weckt Thread B.
  3. Thread B beginnt auf CPU 2 zu laufen.
  4. Thread A geht sofort in den Ruhezustand und wird von Thread B geweckt, wenn Thread B seine aktuelle Arbeit beendet hat.

Eine häufige Overhead-Quelle liegt zwischen den Schritten 2 und 3. Wenn CPU 2 im Leerlauf ist, muss sie wieder in einen aktiven Zustand versetzt werden, bevor Thread B ausgeführt werden kann. Abhängig vom SOC und der Leerlaufzeit kann es mehrere zehn Mikrosekunden dauern, bis Thread B mit der Ausführung beginnt. Wenn die tatsächliche Laufzeit jeder Seite des IPC nahe genug am Overhead liegt, kann die Gesamtleistung dieser Pipeline durch CPU-Leerlaufübergänge erheblich reduziert werden. Am häufigsten tritt dies bei Android bei Binder-Transaktionen auf, und viele Dienste, die Binder verwenden, sehen am Ende wie die oben beschriebene Situation aus.

Verwenden Sie zunächst die Funktion wake_up_interruptible_sync() in Ihren Kernel-Treibern und unterstützen Sie diese von jedem benutzerdefinierten Scheduler aus. Betrachten Sie dies als eine Anforderung, nicht als einen Hinweis. Binder verwendet dies heute und es hilft sehr bei synchronen Binder-Transaktionen und vermeidet unnötige CPU-Leerlaufübergänge.

Stellen Sie zweitens sicher, dass Ihre CPU-Übergangszeiten realistisch sind und der CPU-Governor diese korrekt berücksichtigt. Wenn Ihr SOC immer wieder in den tiefsten Leerlaufzustand wechselt, sparen Sie keinen Strom, wenn Sie in den tiefsten Leerlaufmodus wechseln.

Protokollierung

Die Protokollierung ist weder für CPU-Zyklen noch für den Arbeitsspeicher kostenlos. Spammen Sie daher den Protokollpuffer nicht. Die Protokollierung kostet Zyklen in Ihrer Anwendung (direkt) und im Protokolldämon. Entfernen Sie alle Debugprotokolle, bevor Sie Ihr Gerät versenden.

E/A-Probleme

E/A-Vorgänge sind häufige Ursachen für Jitter. Wenn ein Thread auf eine speicherzugeordnete Datei zugreift und sich die Seite nicht im Seitencache befindet, tritt ein Fehler auf und die Seite wird von der Festplatte gelesen. Dies blockiert den Thread (normalerweise für mehr als 10 ms) und kann, wenn es im kritischen Pfad des UI-Renderings geschieht, zu Ruckeln führen. Es gibt zu viele Ursachen für E/A-Vorgänge, um sie hier zu diskutieren. Überprüfen Sie jedoch die folgenden Stellen, wenn Sie versuchen, das E/A-Verhalten zu verbessern:

  • PinnerService . PinnerService wurde in Android 7.0 hinzugefügt und ermöglicht es dem Framework, einige Dateien im Seitencache zu sperren. Dadurch wird der Speicher für die Verwendung durch andere Prozesse entfernt. Wenn jedoch einige Dateien vorhanden sind, von denen a priori bekannt ist, dass sie regelmäßig verwendet werden, kann es effektiv sein, diese Dateien mit einem Mlock zu versehen.

    Auf Pixel- und Nexus 6P-Geräten mit Android 7.0 haben wir vier Dateien blockiert:
    • /system/framework/arm64/boot-framework.oat
    • /system/framework/oat/arm64/services.odex
    • /system/framework/arm64/boot.oat
    • /system/framework/arm64/boot-core-libart.oat
    Diese Dateien werden von den meisten Anwendungen und system_server ständig verwendet und sollten daher nicht ausgelagert werden. Insbesondere haben wir festgestellt, dass, wenn einige davon ausgelagert werden, sie wieder eingelagert werden und beim Wechsel von einer Schwergewichtsanwendung zu Störungen führen.
  • Verschlüsselung . Eine weitere mögliche Ursache für E/A-Probleme. Wir sind der Meinung, dass die Inline-Verschlüsselung im Vergleich zur CPU-basierten Verschlüsselung oder der Verwendung eines über DMA zugänglichen Hardwareblocks die beste Leistung bietet. Am wichtigsten ist, dass die Inline-Verschlüsselung den mit I/O verbundenen Jitter reduziert, insbesondere im Vergleich zur CPU-basierten Verschlüsselung. Da Abrufe in den Seitencache häufig im kritischen Pfad des UI-Renderings erfolgen, führt die CPU-basierte Verschlüsselung zu zusätzlicher CPU-Last im kritischen Pfad, was zu mehr Jitter führt als nur der E/A-Abruf.

    DMA-basierte Hardware-Verschlüsselungs-Engines haben ein ähnliches Problem, da der Kernel Zyklen damit verbringen muss, diese Arbeit zu verwalten, selbst wenn andere kritische Arbeiten zur Ausführung verfügbar sind. Wir empfehlen jedem SOC-Anbieter dringend, neue Hardware mit Unterstützung für Inline-Verschlüsselung zu entwickeln.

Aggressives Packen kleiner Aufgaben

Einige Planer bieten Unterstützung für das Packen kleiner Aufgaben auf einzelne CPU-Kerne, um den Stromverbrauch zu senken, indem mehr CPUs länger im Leerlauf bleiben. Während dies für den Durchsatz und den Stromverbrauch gut funktioniert, kann es für die Latenz katastrophal sein. Im kritischen Pfad des UI-Renderings gibt es mehrere Threads mit kurzer Laufzeit, die als klein angesehen werden können. Wenn diese Threads verzögert werden, da sie langsam auf andere CPUs migriert werden, führt dies zu Störungen. Wir empfehlen, beim Packen kleiner Aufgaben sehr konservativ vorzugehen.

Überlastung des Seiten-Cache

Ein Gerät ohne ausreichend freien Speicher kann plötzlich extrem träge werden, wenn ein Vorgang mit langer Laufzeit ausgeführt wird, beispielsweise das Öffnen einer neuen Anwendung. Eine Ablaufverfolgung der Anwendung kann zeigen, dass die E/A während eines bestimmten Laufs dauerhaft blockiert ist, auch wenn die E/A häufig nicht blockiert ist. Dies ist normalerweise ein Zeichen für eine Überlastung des Seitencaches, insbesondere auf Geräten mit weniger Speicher.

Eine Möglichkeit, dies zu identifizieren, besteht darin, mithilfe des Pagecache-Tags einen Systrace zu erstellen und diesen Trace dem Skript unter system/extras/pagecache/pagecache.py zuzuführen. pagecache.py übersetzt einzelne Anfragen zum Zuordnen von Dateien in den Seitencache in aggregierte Statistiken pro Datei. Wenn Sie feststellen, dass mehr Bytes einer Datei gelesen wurden, als die Gesamtgröße dieser Datei auf der Festplatte beträgt, liegt ein Seiten-Cache-Thrashing vor.

Dies bedeutet, dass der für Ihre Arbeitslast erforderliche Arbeitssatz (normalerweise eine einzelne Anwendung plus Systemserver) größer ist als die für den Seitencache auf Ihrem Gerät verfügbare Speichermenge. Dies führt dazu, dass ein Teil der Arbeitslast die benötigten Daten im Seitencache erhält, ein anderer Teil, der in naher Zukunft verwendet wird, entfernt wird und erneut abgerufen werden muss, wodurch das Problem bis zum Laden erneut auftritt hat vervollständigt. Dies ist die Hauptursache für Leistungsprobleme, wenn auf einem Gerät nicht genügend Speicher verfügbar ist.

Es gibt keine narrensichere Möglichkeit, Seiten-Cache-Thrashing zu beheben, aber es gibt einige Möglichkeiten, dies auf einem bestimmten Gerät zu verbessern.

  • Benutzen Sie bei persistenten Prozessen weniger Speicher. Je weniger Speicher von persistenten Prozessen verwendet wird, desto mehr Speicher steht Anwendungen und dem Seitencache zur Verfügung.
  • Überprüfen Sie die Ausgliederungen, die Sie für Ihr Gerät haben, um sicherzustellen, dass Sie nicht unnötig Speicher aus dem Betriebssystem entfernen. Wir haben Situationen gesehen, in denen Aussparungen, die zum Debuggen verwendet wurden, versehentlich in den Kernelkonfigurationen des Versands belassen wurden, was Dutzende Megabyte an Speicher verbrauchte. Dies kann den Unterschied ausmachen, ob Seiten-Cache-Thrashing auftritt oder nicht, insbesondere auf Geräten mit weniger Speicher.
  • Wenn Sie feststellen, dass der Seitencache in system_server bei kritischen Dateien überlastet ist, sollten Sie erwägen, diese Dateien anzuheften. Dadurch wird der Speicherdruck an anderer Stelle erhöht, das Verhalten kann jedoch so weit verändert werden, dass Thrashing vermieden wird.
  • Optimieren Sie lowmemorykiller erneut, um zu versuchen, mehr Speicher freizuhalten. Die Schwellenwerte von lowmemorykiller basieren sowohl auf dem absoluten freien Speicher als auch auf dem Seitencache. Daher kann eine Erhöhung des Schwellenwerts, bei dem Prozesse auf einer bestimmten oom_adj-Ebene beendet werden, zu einem besseren Verhalten auf Kosten eines erhöhten Hintergrund-App-Todes führen.
  • Versuchen Sie es mit ZRAM. Wir verwenden ZRAM auf Pixel, obwohl Pixel über 4 GB verfügt, da dies bei selten verwendeten schmutzigen Seiten hilfreich sein könnte.