Az iPhone vs. Android konferencián tartott workshop-om "prezentációja".
- Látványos eredmény főleg UIKit optimalizációval és ARM Assembly-vel.
- Ha egyébként rendben van a kód.
Hogyan mérjük a terhelést?
- Instruments: elsősorban RAM, szivárgás és Core Animation mérésre. CPU-hoz nem jó, abból eleve 35%-ot eszik.
- Shark: ha már minden kötél szakad, vagy az OS funkciói is érdekesek.
- Megoldás: jailbreak, SSH és top. 10% CPU.
- A top-ban csak a teljes, rendszerszintű idle időt nézzük.
- Könnyű "átvinni" a terhelést a saját kódból az OS-hez (user % -> sys %), de ez nem szabadít fel semmit.
Ökölszabály
25-30% szabad CPU mindig kell, különben "akadozik" az élmény.
NSRunLoop
- A main thread NSRunLoop-ja fontos.
- Többek között ez fogadja a touch eseményeket és vezérli a legtöbb NSTimer-t.
- Ha nincs 25-30% szabad CPU, akkor későn fut, későn érkeznek az események - akad.
- Main thread/main run loop blokkolására ügyelni, események lehető leggyorsabb feldolgozása, külön thread, ha kell.
- NSRunLoop vezérli, ezért egy NSTimer sohasem pontos, "csúszkál".
UIKit
- Állítólag 2D "hagyományos", "nyomkodható" felületeknél az UIKit ugyanolyan gyors, mint az OpenGL.
- És sokkal kényelmesebb (Interface Builder).
- Egy UIKit objektum (pl. UIButton) módosítása "költséges", az UIKit a legtöbb esetben blokkol, amíg be nem fejezte a grafikát.
- Összegyűjteni a módosításokat (pl. UIButton.highlighted), és végrehajtani később, nem a touch event kezelésekor.
- CADisplayLink objektummal!
- Az iPhone utálja az átlátszóságot, alpha csatornákat, belassul.
- A legtöbb objektum átlátszatlan legyen, opaque-ra Interface Builder-ben pipa (igen, végig kell kattintgatni az összeset).
- Még így se "halmozzuk egymásra" az objektumokat.
- Ha opaque egy objektum, de alpha-s PNG kép van benne, akkor átlátszó lesz így is...
- Irtsuk ki az alpha csatornát a felhasznált képeknél, ha lehet.
- Ami nem látszik, az legyen .hidden = YES (elég a szülőt), különben fölös CPU megy a touch eventek fogadására.
- A szövegek, UILabel módosítása költséges. Saját drawRect metódus és [self.text drawInRect: sokat segít.
- A saját drawRect metódusok sokszor gyorsabbak.
- Instruments - Core Animation - Flash Updated Regions. Esetenként meg fogunk lepődni.
- A fenti UIKit módosításokkal 160-180%-ról 90%-ra esett vissza a DJ PLAYER erőforrásigénye.
Thumb mód
- ARM CPU-k, kétféle futási mód: Thumb és ARM.
- Thumb mód: kisebb kód és más utasításkészlet, ezért kisebb memória sávszél igény - gyorsabb futás, régi eszközökön (nem iPhone).
- Nem tud lebegőpontos számítást, ilyenkor a fordító átkapcsolja a CPU-t ARM módba, aztán pedig vissza. Ez lassú.
- iPhone: a Thumb mód alapértelmezett, kapcsoljuk ki. Iterációknál sokat számíthat.
Iterációk
- Nézzük meg, hogy a compiler milyen kódot gyárt.
- Elé és mögé inline asm sorok ( __asm__ volatile ("#innen"); ), majd pedig Build - Show Assembly Code.
- Próba: "register" kulcsszó néhány fontos változónál, mennyit változik az Assembly kód. Nem kell annyit töltögetni a memóriából.
- A compiler nem mindig jó a regiszterek hatékony elosztásában, csak néhányat használ.
- Egyszeres lebegőpontos számokat érdemes használni (sima, 32-bites float), hogy a compiler használja a lebegőpontos egységet.
Assembly
- XCode-ban lehet inline Assembly-t írni.
- A C-változatot is meg kell hagyni, mert a Simulatorhoz felesleges külön Assembly verzió.
- A Simulator x86-os, a régebbi iPhone-ok ARM6-os, az újabbak pedig ARM7-es kódot kérnek.
ARM CPU
- Alapvetően kétféle iPhone létezik.
- Régi: iPhone 2G/3G, iPod touch 1G/2G, 400 Mhz ARM6 architektúra, 128 MB RAM.
- Az ARM architektúra nem egyenlő az ARM család számozásával (pl. iPhone 2G: ARM11).
- Vadhajtás: az iPod touch 2G 533 Mhz-en fut.
- Új: iPhone 3GS, iPod touch 3G, 600 Mhz ARM7 architektúra, 256 MB RAM.
- iPad: ARM7 architektúra, 1 GHz, 256 MB RAM ("gyors iPhone 3GS").
- Tehát a fejlesztés során alapvetően ARM6 és ARM7 eszközökkel kell foglalkozni.
Lebegőpontos egység
- "Beépített" segédprocesszor, lebegőpontos számításokra, főleg vektorokhoz, mátrixokhoz.
- ARM6: VFP (Vector Floating Point Unit), "VFP utasításkészlet".
- ARM7: NEON, "NEON utasítások", multimédiára (SIMD).
- A VFP-s Assembly kód általában lassabban fut az iPhone 3GS-en, mint a C változat!
- Megoldás: universal binary app (build settings - Architectures - Optimized (armv6 armv7)).
- Egy kódrészlet így valószínűleg háromféle lesz: C (Simulator), VFP (régi iPhone), NEON (új iPhone).
- Szerencsére a legtöbb ARM7-es VFP utasítás csak az ARM6-os változat "átnevezése" (pl. fmuls - vmul.f32).
- Az Assembly-be átírás nem triviális, ne "automatikusan" csináljuk.
- A regiszterek hatékony kihasználása, a lehető legkevesebb memóriahozzáférés (és lehetőleg blokkban), valamint az utasítások ciklusszámának ismerete akár négyszeres sebességkülönbséget is jelenthet!
- Minden utasítást adott számú ciklus alatt hajt végre a processzor, de képes többet párhuzamosan is!
- Egy VFP szorzás pl. 8 ciklus alatt megy végbe, ez idő alatt mást is csinálhat a CPU, ha a következő utasítás nem a szorzás által használt regisztereken dolgozik (párhuzamosítás).
DJ PLAYER keverő (mixer) kód
- C: 18% CPU.
- VFP Assembly, a ciklusszámok figyelembe vétele nélkül: 8% CPU.
- Végleges, optimalizált változat: 2% CPU.
iPhone OS 4.0
- Accelerate Framework: az Apple megírta helyettünk egy csomó gyakran használt számítás Assembly változatait, és ezeket egyszerű C függvényekként tették elérhetővé.
- Komplex számításokhoz valószínűleg nem jó, a "kézzel gyártott" Assembly kód (a már említett hatékony regiszterhasználat, ciklusszámok, satöbbik miatt) mindig gyorsabb lesz.