Uvnitř Snow Leoparda: Jak Apple docílil zrychlení startu aplikací

Uvnitř Snow Leoparda: Jak Apple docílil zrychlení startu aplikací

Tentokráte se podíváme trochu hlouběji do Mac OS X 10.6, a bude to i z jiného zdroje než AppleInsider. Na stránkách Hamster Emporium se totiž objevil zajímavý článek, který popisuje změny, které Apple provedl v dyld.

Mac OS X Snow Leopard snížil o polovinu startovní čas pro Objective-C runtime a současně ušetřil několik set KB paměti na aplikaci. A vše je k dispozici zdarma pro každou aplikaci, díky pár kouskům Mac OS X, které stojí za každým Objective-C kódem: dyld.

dyld a sdílená cache

Dyld je knihovna umožňující dynamické nahrávání a spojovaní jednotlivých kousků kódu (volně a trochu nepřesně přeloženo). Když startuje aplikace, dyld nahrává spustitelný kód a jeho sdílené knihovny do paměti, spojuje je s knihovnami a proměnnými v C a pak pokračuje spuštěním kódu ve funkci main().

Teoreticky může být sdílená knihovna jinde při každém spuštění programu. Prakticky dostanete stejnou verzi sdílené knihovny téměř po každém spuštění a stejně tak každý další proces v systému. Systém totiž využívá výhod dyld sdílené cache. Sdílená cache obsahuje kopii hlavních systémových knihoven u kterých dyld již provedl spojování a nahrávání v předstihu. Každý proces tak může použít sdílenou cache a ušetřit paměť při startu.

(Zcela náhodou sdílená cache kompletně překonala prebinding systém z dob před Leopardem, který měl provádět stejné optimalizace. Pamatujete si na hlášku při instalaci "Optimizing System Performance", krok který trval déle než vlastní instalace? To byla aktualizace prebinding informací. Přestavění sdílené knihovny je nyní tak rychlé, že se instalátor ani neobtěžuje to hlásit.)

Objective-C unikátní selector

Leopardí dyld sdílená cache je skvělá pro kód v C, ale nepomáhá při startu kódu v Objective-C. Jediným velkým nákladem pro Objective-C je unikátní selektor. Aplikace a každá sdílená knihovna obsahuje vlastní kopii názvu selektorů jako "alloc" a "init". Při spuštění je třeba mít jednoznačný kanonický SEL ukazatel pro každý selektor a aktualizovaná metadata pro každé volání. To znamená sestavení obrovské hash tabulky (v paměti) a volání strcmp() (mnohokrát) a změnu copy-on-write metadat (další paměť)

V běžném procesu jsou desítky tisíc unikátních selektorů. Jestliže spustíte v Leopardovi příkaz "strings /usr/lib/libobjc.dylib" uvidíte třicettisíc řádků vestavěné tabulky selektorů, která byla předchozím pokusem o snížení paměťových nároků. Navíc veškeré náklady rostou s každou novou třídou a metodou přidanou do Cocoa frameworku, pokud by se s tím nic neudělalo, každý upgrade operačního systému by si vyžadoval stále větší paměťové nároky.

A řešení? Práci pro unikátní selektory ponecháme na dyld sdílené cachi. Tabulka selektorů je vestavěná do samotné sdílené knihovny, aktualizace selektoru  se objevují kopii cache sdílené knihovny. Díky tomu ušetříte paměť, protože každý proces sdílí stejnou tabulku selektorů a ušetříte také čas, protože se při spuštění nemusí vždy vytvořit nová. Při spuštění se tak opraví odkazy na selektory z vlastní aplikace. A kde je háček? Selektory jsou příliš dynamické aby je bylo možné implemenotvat jako C symboly, takže nástroj vytvářející sdílenou cache bylo nutné naučit psát a zapisovat Objective-C metadata.

Optimalizace WIN

Snow Leopard dyld shared cache vytváří unikátní Objective-C selektory a Objective-C runtime rozpozná když je selektor ve sdílené knihovně unikátní díky laskavosti sdílené cache. Přes polovinu inicilazačního času je eliminováno, což umožňuje rychlé spuštění aplikace. Typická úspora je okolo 200-500 KB na proces, což znamená několik MB na celý systém. Když se optimalizace přesune na iPhone OS, očekává se úspora 1MB na 128MB zařízení. Tým zodpovědný za výkon iPhone by dal jakékoliv množství rukou a nohou za takový zisk.

Můžete se podívat jak to vypadá ve vašem systému pomocí následujících příkazů:

$ sudo /usr/bin/update_dyld_shared_cache -debug -verify
[...]
update_dyld_shared_cache: for x86_64, uniquing objc selectors
update_dyld_shared_cache: for x86_64, found 68761 unique objc selectors
update_dyld_shared_cache: for x86_64, 541736/590908 bytes (91%) used in libobjc unique selector section
update_dyld_shared_cache: for x86_64, updated 205230 selector references

$ OBJC_PRINT_PREOPTIMIZATION=YES /usr/bin/defaults
objc[424]: PREOPTIMIZATION: selector preoptimization ENABLED (version 3)
objc[424]: PREOPTIMIZATION: honoring preoptimized selectors in /usr/lib/libobjc.A.dylib
objc[424]: PREOPTIMIZATION: honoring preoptimized selectors in /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
objc[424]: PREOPTIMIZATION: honoring preoptimized selectors in /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata<
objc[424]: PREOPTIMIZATION: honoring preoptimized selectors in /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation

Můžete odhadnout úsporu paměti pomocí nástroje allmemory. Podívejte se na využití paměti aplikací s a bez proměnné OBJC_DISABLE_PREOPTIMALIZATION = YES. Podívejte se na "dirty pages", každá "dirty page" má 4KB. S 64-bit TextEdit můžete vidět, že počet stránek skáče z 725 na 1069 po vypnutí optimalizace. To je trochu nadsazené - mnoho z těchto stránek by v Leopardovi neexistovalo kvůli vestavěné tabulce selektorů - ale ukazuje na rozsah úspěchu.

Objective-C runtime dělá mnohem více než jen práci se selektory. Budoucí vylepšení v dyld sdílené cachi může predikovat některou další práci a ještě více vylepšit startovací čas, ušetřit paměť a redukovat cenu linkování Objective-C kódu, který nepoužíváte. Ale proces práce se selektory ve Snow Leopardu je opravdu obrovským skokem kupředu.

 

Odkazy z novinek:

Poslat Uvnitř Snow Leoparda: Jak Apple docílil zrychlení startu aplikací na facebook
Publikováno 30.11.2008
 

Změna barev | Autorská práva | Kontakt | Podpora | RSS kanály
© 2006 Gandalf, Design by Mirek
Creative Commons License