Wieloprocesowość
Od czasu pojawienia się Chrome, temat korzystania z wielu procesów do wyświetlania stron w przeglądarce stał się jednym z ważniejszych przy porównywaniu przeglądarek.
Co więcej, wbrew uwagom samych autorów, powszechnie zaczęto uznawać to jako tzw. “feature” – funkcję, której posiadanie jest bezwględnie objawem przewagi danej przeglądarki nad inną.
Czarne strony
Tymczasem Sam Allen opublikował wynik swojego mini-badania w którym przetestował popularne przeglądarki w zakresie zużycia pamięci. Oto wykres:
Sam przetestował Operę, Firefoksa, Safari i Chrome podczas wczytywania 150 najpopularniejszych stron z rankingu Alexa.com. Opublikował też wyniki w postaci tabeli najwyższego i najniższego zużycia pamięci przez różne przeglądarki.
Sam wykres jest na pewno ciekawy, ale jeszcze ciekawsze są jego implikacje, które potwierdzają pierwsze komentarze jakie pojawiły się w społeczności pracującej nad Gecko po wydaniu Chrome.
Dlaczego tak się dzieje? Wykorzystanie wielu procesów oznacza, że 10 kart z otworzonym google.com będzie dziesięć razy ładować absolutnie wszystko konieczne do wyświetlenia tej strony. To zaś oznacza znaczące koszty w wykorzystaniu pamięci. W “tradycyjnym” modelu, pamięć jest współdzielona i obrazki, struktury danych oraz bloki używane do rysowania układu strony mogą być współdzielone. Nawet obiekty używane do wyrenderowania strony, mogą zostać utworzone raz i uruchamiane podczas rysowania różnych stron.
Tymczasem w przypadku modelu w którym każda strona (czy karta) ma swój proces, musi ona praktycznie załadować do pamięci całą przeglądarkę – a przynajmniej cały kod potrzebny do obługi jednej strony. To kosztuje i to wcale nie mało.
Białe strony
Czy to oznacza, że programiści Chrome popełnili błąd decydując się na taką zmianę? Nie. Oni po prostu zdecydowali się zapłacić taką cenę, za liczne udogodnienia jakie daje rozdział procesów.
Najważniejszym, i pewnie najbardziej zrozumiałym jest możliwość izolacji procesu na wypadek problemów. Jeśli strona spowoduje poważny błąd, będzie to tylko błąd tej karty, a nie całej przeglądarki. Nie stracimy pisanego od godziny listu i nie będziemy musieli uruchamiać wszystkiego od nowa. To duża wygoda.
Inną sprawą jest rozdzielenie pamięci. Strony można uruchamiać w tzw. “piaskownicach”, gdzie są oddzielone od sektorów pamięci wykorzystywanych przez przeglądarkę. W takiej sytuacji nawet jeśli w przeglądarce znajdzie się błąd bezpieczeństwa, to jego wykorzystanie praktycznie zawsze będzie ograniczone do manipulacji danym procesem, a nie całą przeglądarką czy systemem operacyjnym. To ważne udogodnienie.
Istnieją inne, jak łatwe wykorzystanie wielu procesorów i przeniesienie na system operacyjny zarządzania przydzielaniem procesom cykli procesora, co oznacza, że z łatwością można określić, że przeglądarka ma wyższy priorytet niż, np. filmik na youtube i nie będzie on już “przycinał” jej.
Szukanie balansu
W Mozilli powstał specjalny projekt, który eksperymentuje z wykorzystaniem wieloprocesowości. Pierwsze eksperymenty są już testowane i poniżej prezentuję filmik od Chrisa Blizzarda z demonstracji tej technologii:
Najważniejszym jednak pytaniem jest jak wykorzystać wieloprocesowość, aby zmaksymalizować zyski i zminimalizować koszty. Jednym z pomysłów jest uruchamianie rozszerzeń w osobnych procesach – to zwiększa bezpieczeństwo i pozwala oddzielić stabilność aplikacji od rozszerzenia, ale kosztem pamięci. Innym pomysłem jest wydzielenie procesów na wtyczki, ponieważ w nich często znajdowane są błędy bezpieczeństwa a także one najczęściej zawieszają przeglądarki.
Jeszcze innym jest utrzymywanie silnika renderującego w osobnym procesie a stron w osobnych. Wówczas strona wysyłana by była przez kartę do procesu renderującego, który zwracałby wyrenderowaną. W takim układzie karty są niezależne, jeśli jakaś operacja na jednej spowoduje błąd to nie dotknie on innych, a zużycie pamięci nie powinno ucierpieć, ponieważ kod renderujący byłby współdzielony. Niestety taki układ nie daje tych samych zalet w zakresie stabilności. Jeśli silnik rednerujący wyłoży się na jednej stronie, to padnie cały. Jeśli jednak będzie osobnym procesem, to nie przełoży się to na przeglądarkę…
Generalnie temat do rozważań jest potężny i jest mnóstwo miejsca na eksperymenty. Jeśli ktoś jest zainteresowany pracą zespołu, to zapraszam na serwer irc.mozilla.org, kanał #content.
6 replies on “Wielowątkowość w przeglądarce”
“Tymczasem w przypadku modelu w którym każda strona (czy karta) ma swój proces, musi ona praktycznie załadować do pamięci całą przeglądarkę – a przynajmniej cały kod potrzebny do obługi jednej strony. To kosztuje i to wcale nie mało.”
Nie znam zarządzania pamięcią w Windowsie ale na 99% kod i inne dane statyczne będą w pamięci współdzielonej więc mimo że program zlicza je za każdym razem to w rzeczywistości to jeden obszar pamięci.
Maciej: problem jest taki, że w pamięci współdzielonej mogą być tylko dane, które nie są izolowane. A Chrome izoluje całe strony.
“musi ona praktycznie załadować do pamięci całą przeglądarkę” – nie zaprzeczam że strona musi być izolowana – ale mówienie o ładowaniu całej przeglądarki to spora przesada. U mnie różnica między pamięcią przypisaną do procesu i pamięcią współdzieloną bywa 100 MiB na process. Ponieważ mam ok. 100 procesów to wypada że zajęcie pamięci przez to jest jakieś 10000 MiB – czyli jakieś 9.77 GiB. Skądinąd wiem że pomijając cache i bufory zajęte mam 674 MiB pamięci – łącznie z tą przypisaną do procesów. Przesadzam sporo ale zsumowanie 10 najbardziej vmem-ożernych procesów daje więcej niż mam pamięci. Kłopot w tym że swap nie jest zajęty. Więc taka oszczędność ma spory wpływ – a przecież 10 najbardziej vmem-ożernych u mnie procesów nie mają dużo wspólnych danych użytkownika.
(UPDATE: Zliczyłem vmem i wyszło ok. 4.66 GiB. Skoninąd wiem że mam 2 GiB RAMu i nic na swapie nie siedzi)
Ale nie ma prostej możliwości wyizolowania pamięci współdzielonej. Więc metodą byłoby patrzenie na zajęcie pamięci fizycznej przez komputer. Dla uniknięcia błędów trzeba by wyłączyć wszystkie usługi itp. Miałoby to chyba tą jeszcze zaletę że pokazywałoby pamięć zajmowaną przez inne usługi zktórymi przeglądarka komunikuje się przez COM czy podobne technologie (możliwe że także pamięć jądra).
Zapomniałem dodać -nie mówię że Chrome nie jest pamięciożerna ale taki test całkowicie ignoruje jakiekolwiek zarządzanie pamięcią przez system. Chrome ma systematycznie zawyżane zużycie pamięci i nie do końca jest wiadome o ile – ale można przewidywać że nie jest to mała liczba.
Dodatkowo współdzielenie pamięci nie musi oznaczać brak izolacji dzięki kopii-podczas-zapisu (copy-on-write). Np. wywołanie fork jest stosunkowo tanie gdyż domyślnie cała pamięć nie jest kopiowana – kopiowanie następuje dopiero gdy proces spróbuje po niej pisać (otrzymuje wtedy kopię strony tylko dla siebie). Jest to technika stosowana ‘od niepamiętnych czasów’. Zresztą ponieważ fork jest tanie to podprocesy tworzy się na *nixach poprzez fork+execv – ponieważ nic nie jest kopiowane oprócz małego fragmentu stosu.
Oczywiście DOM nie jest wspódzielony – ale i na Fx nie jest bo każda strona ma inny. Pliki w pamięci nawet gdyby nie były wspódzielone to nie grają roli. Nie widzę żadnej ciężkiej rzeczy która mogłaby być tak do końca współdzielona – oprócz możeJIT-owego kodu z JavaScriptu. Reszta albo i tak powinna być RO albo i tak nie może być współdzielona. Ten konkretny paragraf jest pisany z pełną nieodpowiedzialnością gdyż lepiej znam się na pamięci wirtualnej niż na przeglądarkach.
Masz z pewnością racje, że wyniki Chrome są zawyżane, sami autorzy Chrome potwierdzają, że Chrome zużywa więcej pamięci od Firefoksa zarówno w trybie izolacji procesów jak i w trybie jednego procesu (da się uruchomić tak Chrome).
Natomiast niewątpliwie Chrome w trybie wieloprocesowym zużywa zauważalnie więcej ramu niż w trybie jednoprocesowym i tylko tego chciałem dowieść – że wieloprocesowość ma koszty (o tym też piszą autorzy Chrome w podlinkowanym artykule).
Popełnił pan błąd w swoich obliczeniach. Chrome wykorzystuje znaczne ilości pamięci dzielonej. Przykładowo nie ma sensu ładować grafiki 2 lub 3 razy gdy jest ona statyczna. Poszczególne procesy mają więc wskaźnik na to samo miejsce w pamięci. Podobnie jest ze wszystkimi zasobami które są traktowane jako niezmienne.
Jedyne dane które mógłby być dublowane pomiędzy procesami to dane w tzw. sesji. I tutaj jednak nie jest tak jak można byłoby się obawiać. Chrome nie uruchamia każdej zakładki jako osobny proces. Uruchamia każdą witrynę jako osobny proces. Dzięki temu Chrome zapobiega wykradaniu np. haseł przez skrypty wykonujące się w osobnych zakładkach. W ramach jednej sesji dowolna ilość zakładek z tego samego serwisu działa w pojedynczym procesie, dane nie są więc dublowane.
Jak więc zliczyć pamięć? Najlepiej sprawdzić bezpośrednio w Chrome, jest tam zakładka obsługi procesów. Alternatywą jest sprawdzenie w systemie wolnej pamięci: przed uruchomieniem testu, w trakcie i po nim. Sprawdzenie pamięci wolnej nie wykorzystywanej przez procesy gwarantuje że system pokaże realnie o ile ilość wolnej pamięci się zmniejszyła. Przed takim testem najlepiej zablokować wszystkie niekrytyczne usługi i aplikacje.