Jitter to przypadkowe zachowanie systemu, które uniemożliwia prawidłowe działanie. Na tej stronie opisujemy, jak wykrywać i rozwiązywać problemy związane z zakłóceniami wynikającymi z niestabilności.
Opóźnienie harmonogramu wątku aplikacji
Opóźnienie planisty jest najbardziej oczywistym objawem jittera: proces, który powinien zostać uruchomiony, staje się możliwy do uruchomienia, ale nie jest uruchamiany przez pewien znaczący czas. Znaczenie opóźnienia zależy od kontekstu. Przykład:
- Losowy wątek pomocniczy w aplikacji może być opóźniony o kilka milisekund bez żadnych problemów.
- Wątek interfejsu aplikacji może tolerować jitter o długości 1–2 ms.
- Pętle wątków sterownika działające jako SCHED_FIFO mogą powodować problemy, jeśli są uruchamiane przez 500 us przed uruchomieniem.
Czasy wykonania można rozpoznać w systrace po niebieskim pasku poprzedzającym działający segment wątku. Czas działania może być też określony na podstawie czasu między zdarzeniem sched_wakeup
dla wątku a zdarzeniem sched_switch
, które sygnalizuje rozpoczęcie wykonywania wątku.
wątki, które działają zbyt długo;
Nici interfejsu aplikacji, które są uruchomione zbyt długo, mogą powodować problemy. Niski czas wykonywania wątków na niższym poziomie ma zazwyczaj różne przyczyny, ale próba zmniejszenia czasu wykonywania wątku interfejsu użytkownika do zera może wymagać rozwiązania tych samych problemów, które powodują długi czas wykonywania wątków na niższym poziomie. Aby uniknąć opóźnień:
- Używaj cpusets zgodnie z opisem w artykule Ograniczanie temperatury.
- Zwiększ wartość CONFIG_HZ.
- Dotychczas na platformach ARM i ARM64 wartość była ustawiana na 100. Jest to jednak przypadek historyczny i nie jest to dobra wartość do użycia w przypadku urządzeń interaktywnych. CONFIG_HZ=100 oznacza, że jiffy trwa 10 ms, co oznacza, że równoważenie obciążenia między procesorami może zająć 20 ms (2 jiffies). Może to znacznie pogorszyć płynność działania na załadowanym systemie.
- Najnowsze urządzenia (Nexus 5X, Nexus 6P, Pixel i Pixel XL) są dostarczane z wartością CONFIG_HZ=300. Powinna ona mieć znikome koszty energii, a jednocześnie znacznie poprawić czasy działania. Jeśli po zmianie wartości CONFIG_HZ zauważysz znaczny wzrost zużycia energii lub problemy z wydajnością, prawdopodobnie jeden z Twoich sterowników używał timera opartego na surowych wartościach jiffies zamiast milisekund i przekształcał je w jiffies. Zwykle można to łatwo naprawić (patrz łatka, która rozwiązuje problemy z licznikiem czasu kgsl na Nexusie 5X i 6P podczas konwertowania na CONFIG_HZ=300).
- W ramach eksperymentu użyliśmy wartości CONFIG_HZ=1000 na Nexusie/Pixelu i zauważyliśmy, że zapewnia ona zauważalną poprawę wydajności i obniżenie zużycia energii dzięki zmniejszeniu obciążenia RCU.
Dzięki tym dwóm zmianom czas wykonywania wątku interfejsu powinien być znacznie krótszy.
Użyj polecenia sys.use_fifo_ui.
Aby zredukować czas wykonywania wątku interfejsu do zera, możesz ustawić właściwość sys.use_fifo_ui
na 1.
Ostrzeżenie: nie używaj tej opcji w przypadku konfiguracji z różnymi procesorami, chyba że masz planista RT uwzględniający pojemność.
Obecnie NIE WYSYŁAMY ZEGARKÓW RT SCHEDULER Z POBIERZEM ZDANYM NA ILOŚĆ. Pracujemy nad wersją dla EAS, ale nie jest ona jeszcze dostępna. Domyślny harmonogram RT jest oparty wyłącznie na priorytetach RT i tym, czy procesor ma już wątek RT o takim samym lub wyższym priorytecie.
W rezultacie domyślny harmonogram RT chętnie przeniesie stosunkowo długo działający wątek interfejsu z dużego rdzenia o wysokiej częstotliwości do małego rdzenia o minimalnej częstotliwości, jeśli wątek kfifo o wyższym priorytecie zostanie uruchomiony na tym samym dużym rdzeniu. Spowoduje to znaczne pogorszenie skuteczności. Ta opcja nie była jeszcze używana na urządzeniu z Androidem, dlatego jeśli chcesz z niej skorzystać, skontaktuj się z zespołem ds. wydajności Androida, aby uzyskać pomoc w jej weryfikacji.
Gdy parametr sys.use_fifo_ui
jest włączony, ActivityManager śledzi wątki UI i RenderThread (2 najważniejsze wątki interfejsu użytkownika) w najpopularniejszej aplikacji i przypisuje im priorytet SCHED_FIFO zamiast SCHED_OTHER. To skutecznie eliminuje drgania w interfejsie użytkownika i wątkach renderowania. Ścieżki zebrane przy włączonej opcji pokazują czasy wykonywania rzędu mikrosekund zamiast milisekund.
Jednak ponieważ równoważenie obciążenia w RT nie uwzględniało pojemności, wydajność uruchamiania aplikacji spadła o 30%, ponieważ wątek interfejsu użytkownika odpowiedzialny za uruchamianie aplikacji został przeniesiony z jądra Kryo złotego o częstotliwości 2,1 GHz na srebrne jądro Kryo o częstotliwości 1,5 GHz. Dzięki równoważnikowi obciążenia RT z uwzględnieniem pojemności widzimy równoważną wydajność w operacjach zbiorczych oraz skrócenie czasu generowania klatek w 95. i 99. procencie przypadków w wielu testach porównawczych interfejsu użytkownika o 10–15%.
Przerywanie ruchu
Platformy ARM dostarczają przerwania do procesora 0 tylko domyślnie, dlatego zalecamy używanie wyważnika IRQ (irqbalance lub msm_irqbalance na platformach Qualcomm).
Podczas tworzenia Pixela zaobserwowaliśmy problemy z płynnością, które można było bezpośrednio przypisać do przeciążenia procesora 0 przez przerwania. Jeśli na przykład wątek mdss_fb0
został zaplanowany na procesorze 0, prawdopodobieństwo wystąpienia tarcia było znacznie większe z powodu przerwania wywołanego przez wyświetlacz niemal natychmiast przed zakończeniem skanowania. mdss_fb0
byłaby w trakcie wykonywania swojej pracy z bardzo krótkim terminem, a potem straciłaby trochę czasu na obsługę przerwania MDSS. Początkowo spróbowaliśmy rozwiązać ten problem, ustawiając przynależność procesora wątku mdss_fb0 na procesory 1–3, aby uniknąć konfliktu z przerwą, ale potem zdaliśmy sobie sprawę, że nie włączyliśmy jeszcze opcji msm_irqbalance. Po włączeniu opcji msm_irqbalance zauważalnie zmniejszyło się opóźnienie nawet wtedy, gdy zarówno mdss_fb0, jak i przerwanie MDSS były na tym samym procesorze ze względu na zmniejszoną konkurencję ze strony innych przerwań.
Możesz to sprawdzić w systrace, patrząc na sekcję sched oraz sekcję irq. Sekcja harmonogramu pokazuje, co zostało zaplanowane, ale nakładają się na nią obszary w sekcji irq, co oznacza, że w tym czasie zamiast normalnie zaplanowanego procesu działa przerwanie. Jeśli zauważysz znaczne przerwy w czasie trwania przerwy, możesz:
- przyspieszyć obsługę przerwania,
- Zapobieganie przerwaniu odtwarzania.
- Zmień częstotliwość przerwania, aby nie kolidowała z innymi regularnymi działaniami (jeśli jest to regularne przerwanie).
- Ustaw bezpośrednio powiązanie procesora z przerwą i uniemożliw dostęp do procesora innym procesom.
- Aby uniknąć przerwania, ustaw powiązanie procesora z wątkiem, który jest zakłócany.
- Użyj mechanizmu równoważenia przerwań, aby przenieść przerwanie na procesor o mniejszym obciążeniu.
Ustawianie powiązania procesora z procesorem nie jest ogólnie zalecane, ale może być przydatne w niektórych przypadkach. Ogólnie trudno przewidzieć stan systemu w przypadku większości przerwań, ale jeśli masz bardzo konkretny zestaw warunków, które powodują pewne przerwania, gdy system jest bardziej ograniczony niż zwykle (np. VR), jawna zgodność z procesorem może być dobrym rozwiązaniem.
Długie softirq
Podczas działania softirq wyłącza wywłaszczenie. Może on być również uruchamiany w wielu miejscach w jądrze i może działać w ramach procesu użytkownika. Jeśli aktywność softirq jest wystarczająca, procesy użytkownika przestaną uruchamiać softirq, a ksoftirqd zostanie uruchomiony, aby obsługiwać równoważenie obciążenia. Zwykle nie ma w tym nic złego. Jednak jeden bardzo długi softirq może spowodować poważne problemy w systemie.
softirq są widoczne w sekcji irq śledzenia, więc łatwo je zauważyć, jeśli problem można odtworzyć podczas śledzenia. Ponieważ softirq może działać w ramach procesu użytkownika, zły softirq może też być dodatkowym czasem wykonywania w ramach procesu użytkownika bez oczywistego powodu. Jeśli tak się stanie, sprawdź sekcję irq, aby dowiedzieć się, czy problemem są miękkie przerwania.
Sterowniki pozostawiające wyłączone zastrzeżenia lub przerwania IRQ na zbyt długo
Wyłączenie preemptionu lub przerw zbyt długich (kilkadziesiąt milisekund) powoduje zacinanie. Zazwyczaj zjawisko to objawia się w postaci wątku, który jest gotowy do wykonania, ale nie działa na określonym procesorze, nawet jeśli ma on znacznie wyższy priorytet (lub SCHED_FIFO) niż inny wątek.
Oto kilka wskazówek:
- Jeśli Runnable Thread to SCHED_FIFO, a Running Thread to SCHED_OTHER, oznacza to, że wątek Running Thread ma wyłączone wywłaszczenie lub przerwania.
- Jeśli wątek do wykonania ma znacznie wyższy priorytet (100) niż bieżący wątek (120), prawdopodobnie ma wyłączone przerwania lub zastąpienia, jeśli wątek do wykonania nie zostanie uruchomiony w ciągu 2 cykli.
- Jeśli wątki do uruchomienia i uruchomione mają ten sam priorytet, wątki uruchomione prawdopodobnie mają wyłączone wywłaszczenie lub przerwania, jeśli wątki do uruchomienia nie zostaną uruchomione w ciągu 20 ms.
Pamiętaj, że wykonywanie modułu obsługi przerwania uniemożliwia obsługę innych przerwań, co również wyłącza wymuszanie.
Inną opcją identyfikowania problematycznych regionów jest użycie narzędzia preemptirqsofftracer (patrz Używanie dynamicznego ftrace). Ten wskaźnik może zapewnić znacznie lepsze informacje o przyczynie nieprzerwanych regionów (np. nazw funkcji), ale jego włączenie wymaga bardziej inwazyjnych działań. Może to mieć większy wpływ na wydajność, ale warto spróbować.
Nieprawidłowe użycie kolejek zadań
Obsługa przerwań często musi wykonywać zadania, które mogą być wykonywane poza kontekstem przerwania, co umożliwia przydzielanie pracy do różnych wątków w jądrze. Deweloper sterownika może zauważyć, że jądro ma bardzo wygodną funkcję asynchronicznych zadań systemowych o nazwie workqueues, którą może wykorzystać do pracy związanej z przerwą.
Jednak w przypadku tego problemu kolejki zadań są prawie zawsze błędną odpowiedzią, ponieważ zawsze mają wartość SCHED_OTHER. Wiele przerwań sprzętowych znajduje się na krytycznej ścieżce wydajności i musi być wykonywanych natychmiast. Nie ma gwarancji, kiedy będą one uruchomione. Za każdym razem, gdy zauważyliśmy kolejkę zadań na krytycznym szlaku wydajności, była ona źródłem sporadycznych zakłóceń, niezależnie od urządzenia. Na urządzeniu Pixel z flagowym procesorem zaobserwowaliśmy, że w przypadku dużego obciążenia urządzenia (w zależności od działania harmonogramu i innych procesów działających w systemie) pojedyncza kolej zadań mogła być opóźniona nawet o 7 ms.
Zamiast kolejki zadań sterowniki, które muszą obsługiwać zadania podobne do przerwania w ramach osobnego wątku, powinny tworzyć własne wątki ksched_fifo. Aby dowiedzieć się, jak to zrobić za pomocą funkcji kthread_work, zapoznaj się z tym poprawką.
Rywalizacja o blokadę platformy
Rywalizacja o blokadę frameworku może być źródłem zacięcia lub innych problemów z wydajnością. Jest to zwykle spowodowane blokadą ActivityManagerService, ale może też występować w przypadku innych blokad. Na przykład blokada PowerManagerService może wpływać na wydajność ekranu blokady. Jeśli widzisz ten komunikat na urządzeniu, nie ma skutecznego rozwiązania tego problemu, ponieważ można go poprawić tylko poprzez wprowadzenie zmian w architekturze frameworka. Jeśli jednak modyfikujesz kod, który działa w ramach systemu_server, ważne jest, aby nie blokować przez długi czas, zwłaszcza blokady ActivityManagerService.
Rywalizacja o blokadę bindera
Do tej pory binder miał jeden globalny blokada. Jeśli wątek wykonujący transakcję bindera został zablokowany, żaden inny wątek nie może wykonać transakcji bindera, dopóki oryginalny wątek nie zwolni blokady. To jest złe. Rywalizacja bindera może blokować wszystko w systemie, w tym wysyłanie aktualizacji interfejsu do wyświetlacza (wątki interfejsu komunikują się z SurfaceFlingerem za pomocą bindera).
Android 6.0 zawierał kilka poprawek, które poprawiały to zachowanie przez wyłączenie preempcji podczas przytrzymania blokady wiązania. Było to bezpieczne tylko dlatego, że blokada powinna być utrzymywana przez kilka mikrosekund rzeczywistego czasu działania. To znacznie poprawiło wydajność w niekonkurencyjnych sytuacjach i zapobiegło konfliktom, ponieważ uniemożliwiło większość przełączeń harmonogramu podczas blokowania wiązania. Nie udało się jednak wyłączyć zastrzeżenia na cały czas trwania blokady bindera, co oznacza, że zastrzeżenie było włączone w przypadku funkcji, które mogły przejść w stan uśpienia (takich jak copy_from_user). Mogło to spowodować to samo zastrzeżenie co w pierwotnym przypadku. Gdy wysłaliśmy poprawki do zespołu, który je tworzył, otrzymaliśmy od nich szybką odpowiedź, że to najgorszy pomysł w historii. (zgodzono się z tym, ale nie można było też zaprzeczyć skuteczności poprawek w zapobieganiu zacięciam).
fd contention within a process
Jest to rzadkie. Prawdopodobnie nie jest to przyczyną zjawiska jank.
Jeśli jednak w ramach procesu masz kilka wątków zapisujących ten sam plik fd, możesz zauważyć rywalizację o ten plik. Jedynym przypadkiem, gdy widzieliśmy to podczas uruchamiania Pixela, był test, w którym wątki o niskim priorytecie próbowały wykorzystać cały czas procesora, podczas gdy w ramach tego samego procesu działał jeden wątek o wysokim priorytecie. Wszystkie wątki zapisywały do wskaźnika fd wskaźnika markera śladu, a wątek o wysokim priorytecie mógł zostać zablokowany na wskaźniku fd wskaźnika markera śladu, jeśli wątek o niskim priorytecie blokował wskaźnik fd, a następnie został zablokowany. Gdy śledzenie zostało wyłączone z wątków o niskim priorytecie, nie wystąpił problem ze skutecznością.
Nie udało nam się odtworzyć tego w żadnej innej sytuacji, ale warto wspomnieć, że może to być potencjalna przyczyna problemów z wydajnością podczas śledzenia.
Niepotrzebne przejścia do stanu bezczynności procesora
W przypadku interfejsów IPC, zwłaszcza wieloprocesowych przepływów danych, często występują różnice w zachowaniu w czasie wykonywania dotyczące:
- Wątek A działa na procesorze 1.
- Wątek A budzi wątek B.
- Wątek B zaczyna się wykonywać na procesorze 2.
- Wątek A natychmiast przechodzi w stan uśpienia, aby zostać przebudzony przez wątek B, gdy ten ostatni zakończy bieżącą pracę.
Najczęstszym źródłem kosztów są kroki 2 i 3. Jeśli procesor 2 jest nieaktywny, musi zostać przywrócony do stanu aktywnego, zanim wątek B będzie mógł się uruchomić. W zależności od SOC i głębokości bezczynności może to potrwać kilkadziesiąt mikrosekund, zanim wątek B zacznie działać. Jeśli rzeczywisty czas działania każdej strony interfejsu IPC jest zbliżony do narzutu, ogólna wydajność tego potoku może zostać znacznie obniżona przez przejścia procesora do stanu bezczynności. Najczęstszym miejscem na Androidzie, w którym występuje ten problem, są transakcje bindera, a wiele usług korzystających z bindera kończy się w sytuacji opisanej powyżej.
Najpierw użyj funkcji wake_up_interruptible_sync()
w sterownikach jądra i obsługuj ją za pomocą dowolnego niestandardowego harmonogramu. Traktuj to jako wymóg, a nie wskazówkę. Binder korzysta z tego obecnie, co bardzo pomaga w przypadku asynchronicznych transakcji bindera, ponieważ pozwala uniknąć niepotrzebnych przejść do stanu bezczynności procesora.
Po drugie, upewnij się, że czasy przejścia w stan bezczynności procesora są realistyczne i że sterownik cpuidleprawidłowo je uwzględnia. Jeśli SOC przechodzi w ciągle w górę i w dół między najgłębszym stanem bezczynności, nie zaoszczędzisz energii, przechodząc do tego stanu.
Logowanie
Rejestrowanie nie jest bezpłatne pod względem cykli procesora ani pamięci, więc nie spamuj bufora logów. Zapisywanie danych kosztuje w aplikacji (bezpośrednio) i w demonie logowania. Przed wysłaniem urządzenia usuń wszystkie dzienniki debugowania.
Problemy z wejścia-wyjścia
Operacje wejścia-wyjścia są częstym źródłem jittera. Jeśli wątek uzyskuje dostęp do pliku zmapowanego w pamięci i strona nie znajduje się w pamięci podręcznej strony, powoduje błąd i odczytuje stronę z dysku. Blokuje to wątek (zwykle na co najmniej 10 ms), a jeśli zdarzy się to na ścieżce krytycznej renderowania interfejsu użytkownika, może spowodować zacinanie. Istnieje zbyt wiele przyczyn operacji wejścia/wyjścia, aby omówić je wszystkie, ale jeśli chcesz poprawić działanie operacji wejścia/wyjścia, sprawdź te lokalizacje:
- PinnerService. PinnerService został dodany w Androidzie 7.0 i umożliwia platformie blokowanie niektórych plików w pamięci podręcznej strony. Spowoduje to usunięcie pamięci używanej przez inne procesy, ale jeśli istnieją pliki, które są regularnie używane, można je skutecznie zablokować.
Na urządzeniach Pixel i Nexus 6P z Androidem 7.0 zablokowaliśmy 4 pliki:- /system/framework/arm64/boot-framework.oat
- /system/framework/oat/arm64/services.odex
- /system/framework/arm64/boot.oat
- /system/framework/arm64/boot-core-libart.oat
- Szyfrowanie. Inna możliwa przyczyna problemów z wejściem/wyjściem. Nasze testy wykazały, że szyfrowanie wbudowane zapewnia najlepszą wydajność w porównaniu z szyfrowaniem na procesorze lub z użyciem bloku sprzętowego dostępnego za pomocą DMA. Co najważniejsze, szyfrowanie w wierszu zmniejsza jitter związany z we/wy, zwłaszcza w porównaniu z szyfrowaniem na procesorze. Ponieważ pobieranie do pamięci podręcznej strony często znajduje się na ścieżce krytycznej renderowania interfejsu użytkownika, szyfrowanie na procesorze powoduje dodatkowe obciążenie procesora na ścieżce krytycznej, co powoduje większe drżenie niż tylko pobieranie danych wejścia/wyjścia.
Sprzętowe mechanizmy szyfrowania oparte na DMA mają podobny problem, ponieważ jądro musi poświęcać cykle na zarządzanie tą pracą, nawet jeśli dostępne są inne krytyczne zadania do wykonania. Zdecydowanie zalecamy, aby dostawcy SOC budujący nowe urządzenia uwzględniali obsługę szyfrowania w przepływie danych.
Agresywne pakowanie małych zadań
Niektóre harmonogramy obsługują pakowanie małych zadań na pojedyncze rdzenie procesora, aby zmniejszyć zużycie energii przez dłuższy czas. Chociaż ta metoda sprawdza się w przypadku przepustowości i zużycia energii, może prowadzić do katastrofalnych opóźnień. Na ścieżce krytycznej renderowania interfejsu jest kilka wątków o krótkim czasie działania, które można uznać za małe. Jeśli te wątki są opóźnione, ponieważ są powoli przenoszone na inne procesory, spowodują zakłócenia. Zalecamy bardzo ostrożne pakowanie małych zadań.
nadmierne wykorzystywanie pamięci podręcznej strony,
Urządzenie z niewystarczającą ilością wolnej pamięci może nagle stać się bardzo wolne podczas wykonywania długotrwałej operacji, takiej jak otwieranie nowej aplikacji. Ślad aplikacji może wskazywać, że podczas danego uruchomienia aplikacja jest stale blokowana w operacjach wejścia/wyjścia, nawet jeśli często nie jest blokowana w operacjach wejścia/wyjścia. Jest to zwykle oznaka wyczerpywania pamięci podręcznej strony, zwłaszcza na urządzeniach z małą ilością pamięci.
Jednym ze sposobów na zidentyfikowanie tego problemu jest wykonanie śledzenia systrace za pomocą tagu pagecache i podanie tego śledzenia do skryptu na stronie system/extras/pagecache/pagecache.py
. Skrypt pagecache.py przekształca poszczególne żądania mapowania plików do pamięci podręcznej strony w zbiorcze statystyki dotyczące poszczególnych plików. Jeśli zauważysz, że odczytano więcej bajtów niż całkowity rozmiar pliku na dysku, oznacza to, że masz do czynienia z przepełnieniem pamięci podręcznej strony.
Oznacza to, że zestaw roboczy wymagany przez Twoje zadanie (zazwyczaj jedna aplikacja plus system_server) jest większy niż ilość pamięci dostępnej dla pamięci podręcznej stron na urządzeniu. W efekcie, gdy jedna część obciążenia otrzyma potrzebne dane z pamięci podręcznej strony, druga część, która będzie używana w najbliższej przyszłości, zostanie usunięta i będzie musiała zostać pobrana ponownie, co spowoduje ponowne wystąpienie problemu, dopóki wczytywanie nie zostanie ukończone. Jest to główna przyczyna problemów z wydajnością, gdy na urządzeniu jest za mało pamięci.
Nie ma niezawodnego sposobu na rozwiązanie problemu z przepełnieniem pamięci podręcznej strony, ale istnieje kilka sposobów na poprawę sytuacji na danym urządzeniu.
- Używaj mniej pamięci w procesach trwałych. Im mniej pamięci używają procesy trwałe, tym więcej pamięci jest dostępne dla aplikacji i pamięci podręcznej stron.
- Sprawdź wyjątki dotyczące swojego urządzenia, aby upewnić się, że nie usuwasz niepotrzebnie pamięci z systemu operacyjnego. Zdarzały się sytuacje, w których wycięcia używane do debugowania były przypadkowo pozostawione w konfiguracjach jądra domyślnych, zajmując dziesiątki megabajtów pamięci. Może to mieć znaczenie w przypadku wyczerpywania pamięci podręcznej strony, zwłaszcza na urządzeniach z mniejszą ilością pamięci.
- Jeśli w system_server widzisz nadmierne zużycie pamięci podręcznej strony w przypadku plików krytycznych, rozważ ich przypięcie. Spowoduje to zwiększenie obciążenia pamięci w innych miejscach, ale może też zmienić zachowanie w sposób wystarczający do uniknięcia tłoczenia.
- Zmień ustawienia lowmemorykiller, aby zwolnić więcej pamięci. Próg działania lowmemorykiller zależy zarówno od ilości wolnej pamięci, jak i pamięci podręcznej strony, więc zwiększenie progu, przy którym procesy o danym poziomie oom_adj są zabijane, może poprawić działanie aplikacji kosztem zwiększenia liczby aplikacji działających w tle.
- Wypróbuj ZRAM. Na Pixelu używamy ZRAM-u, mimo że Pixel ma 4 GB, ponieważ może to pomóc w przypadku rzadko używanych stron z nieaktualnymi danymi.