J2ME pro pokročilé – optimalizace zdrojového kódu s profilérem

5. října 2004

V jednom z předchozích článků jsme si teoreticky rozebrali základní techniky, které se v J2ME běžně používají pro optimalizaci kódu. Pokud chceme jít ještě dále, je třeba použít některý ze specializovaných nástrojů, který zjistí, jak jsou zkoumané části kódu náročné na provedení. Jeden takový nástroj je součástí Wireless Tooliktu a my si zde ukážeme, jak se s ním pracuje.

Kdy optimalizovat?

Ještě než se vrhneme na optimalizaci, je třeba si položit otázku, zda má další optimalizace smysl. V základu můžeme rozdělit náročnější aplikace, především hry, do dvou skupin, na Input-Drive a Real-Time. Do první zmíněné skupiny patří hry, které jsou založeny na vstupu uživatele, typickým příkladem jsou karetní hry nebo šachy. Zde příliš nezáleží na rychlosti, proto další optimalizace nemá smysl. Zajímavější skupinou jsou ovšem realtime hry, které jsou typické akcí na displeji telefonu. Od těchto her očekáváme, že budou rychle reagovat na vstup od uživatele, a budou mít dostatečně vysoký framerate (FPS) pro plynulou animaci. Typickým příkladem jsou arkády, akční hry a realtime strategie.

Při optimalizaci je třeba si ale uvědomit, že existují důvody proč neoptimalizovat:

  • optimalizací můžeme do kódu zanést chyby
  • vynaložené úsilí nemusí odpovídat výsledku
  • některé techniky mohou snížit možnost portace na jinou platformu
  • je to zdlouhavá a mravenčí práce

Profilér

Jak již bylo řečeno v úvodu článku, součástí KToolbaru je i nástroj nazývaný Profilér. Spusťte KToolbar, zvolte „edit/preference“ a vyberte kartu „monitoring“. Najděte pole „Enable profiling“ a zaškrtněte jej. Tím máte zajištěno, že při každém spuštění naší aplikace bude spuštěn i profilér.

Než se pustíte dále, stáhněte si nejprve originální zdrojové kódy testovací aplikace a důkladně si je prostudujte. Ze zdrojových kódu je vidět, že funkční přínos je nulový, ale pro naše experimenty to není podstatné. Vytvořte si nový projekt pomocí KToolbaru, jako default zařízení vyberte „DefaultGrayPhone“ a spusťte aplikaci. Proveďte test a zavřete aplikaci. Pokud jste správně nastavili použití profiléru, objeví se vám podobné okno s výsledky.

Okno profiléru s naměřenými daty
Okno profiléru s naměřenými daty (plná velikost, cca 11 kB)

V levé části okna se nachází stromová struktura metod, tak, jak jsou navzájem volány. V pravé části jsou naměřené výsledky. Aby se nám s nimi lépe pracovalo, výsledky si seřadíme. Vyberte z menu „View/Sort By/%Cycles“. Jak vidíte, na prvních třech místech se umístila metoda pro vykreslení textu na displej, která je součásti metody paint (54 %), metoda doModel (12 %) a work (9 %). Procenta v závorce vyjadřují náročnost. Berte tato čísla s rezervou, protože se mění v závislosti na rychlosti PC. My je zde použijeme jako referenční hodnoty, se kterými budeme porovnávat další výsledky. Chceme-li tedy zrychlit naši aplikaci, musíme se zaměřit na tyto tři metody.

Optimalizace

Optimalizaci rozdělíme na dvě části – optimalizace modelu a optimalizace vykreslování.

Optimalizace vykreslování

Vykreslování má na svědomí metoda paint, která je definována takto:

public void paint(Graphics g) {
  g.setColor(BACKGROUND);
  g.fillRect(0, 0, getWidth(), getHeight());
  g.setColor(FOREGROUND);
  g.setFont(Font.getFont(Font.FACE_PROPORTIONAL,
        Font.STYLE_BOLD | Font.STYLE_ITALIC, Font.SIZE_SMALL));
  for (int i = 0; i < DRAWS; i++) {
    g.drawString(frameTime + “ ms per frame“,
        getRandom(getWidth()), getRandom(getHeight()),
        Graphics.TOP | Graphics.HCENTER);
  }
}

Podíváme-li se na kód této metody, zjistíme, že smyčka obsahuje části, které můžeme napsat jako static final. Jsou to tyto části kódu:

  • ve smyčce se stále volají metody pro zjištění velikosti displeje, který má konstantní rozměry
  • neustále se získává tentýž objekt fontu
  • kotva (?; anchor) umístění textu se nemění

Dalším kandidátem na optimalizaci je získání náhodného čísla polohy textu na displeji. Lepší je, pokud nám to paměť dovolí, předpočítat hodnoty do tabulek a pak z nich hodnoty jen vybírat. Přístup do pole je přeci jen rychlejší, než neustálé získávání hodnot.

Největší vrásky nám ale dělá vykreslování vlastního textu. Z předchozího článku je jasné, že je třeba použít StringBuffer. Jeho naplnění provedeme zavedením řetězcové konstanty MESSAGE s textem, který budeme vykreslovat. Abychom si to trochu ulehčili, budeme text (FPS) přidávat na konec této konstanty. Tím se vyhneme neustálému vytváření nebo ne zrovna jednoduchému získání výsledného textu. Protože se první část textu nemění, postačí, když odstraníme starou část a nahradíme ji novou.

Předchozími optimalizacemi jsme se sice zbavili neustálého vytváření objektů, ale ještě to není ono. Proto obsah StringBufferu vykreslíme do externího obrázku a ten pak přeneseme na displej. Abychom neměnili obsah obrázku neustále, vložíme ještě podmínku, která nám zajistí, že se obrázek změní, jen když se změní hodnota FPS.

Metoda paint po optimalizaci:

public void paint(Graphics g) {
  // prepare string
  if (oldTime != frameTime) {
    buffer.delete(messageLength, buffer.length());
    buffer.append(frameTime).toString();
    messageGraphics.setColor(BACKGROUND);
    messageGraphics.fillRect(0, 0, MAX_STRING_WIDTH, MAX_STRING_HEIGHT);
    messageGraphics.setColor(FOREGROUND);
    messageGraphics.drawString(buffer.toString(), 0, 0, MESSAGE_ANCHOR);
  }
  g.setColor(BACKGROUND);
  g.fillRect(0, 0, WIDTH, HEIGHT);
  for (int i = DRAWS; –i >= 0; i++) {
    rnd = (rnd + 1) % MAX_RANDOM;
    g.drawImage(messageImage, randomX[rnd], randomY[rnd], TEXT_ANCHOR);
  }
  oldTime = frameTime;
}

Provedeme-li nyní měření, zjistíme, že náročnost vykreslování klesla přibližně na polovinu, tedy 22 %. O to více se ale projevilo volání metod work() (15 %) a doModel() (11 %).

Optimalizace modelu

Model je založen na třech metodách work(), doModel(int n) a mathOperation(int a, int b, int c). Metoda work() volá ostatní dvě. Nejprve bychom se měli zbavit globálních proměnných tím, že z nich vytvoříme lokální proměnné. Dále vidíme, že volání metody doModel lze přenést do těla první smyčky.

Při psaní kódu se obvykle snažíme, aby se části neopakovaly. Vytvoříme tedy metodu a tu pak voláme z jiných částí kódu. V kritických částech je ale volání metody pomalé a je lepší (pokud je to možné) přenést její tělo přímo do kritického kódu. V našem případě to je metoda mathOperation. Náš případ nám dále umožňuje přepsat smyčky tak, aby se provádělo testování na nulu.

Dalším nezanedbatelným faktorem je i modifikátor metody. Přestože jsou metody work a doModel takzvaně synchronized, můžeme tento modifikátor odstranit, protože neexistuje nikdo jiný než my, kdo bude tuto metodu volat. Zde je seznam modifikátorů, seřazených od nejpomalejšího k nejrychlejšímu:

  • synchronized
  • interface
  • normální volání
  • final
  • static

Metodu work můžeme ještě optimalizovat tím, že rozepíšeme vnější smyčku, která je tvořena osmi cykly. I když se může zdát, že je to mrhání místem, je třeba si uvědomit, že výsledný kód se komprimuje a opakované části se komprimují zvlášť dobře.

Výsledky

Stáhněte si modifikované zdrojové kódy a porovnejte si je s původními. Otestujeme-li původní kód na reálném zařízení (použil jsem SE T610, firmware R3C002), získáme výsledek přibližně 12 405, po optimalizaci je výsledek 5 920. Což je o více než polovinu lepší. Několik obecných rad pro optimalizaci:

  • Používejte profilér, který vám ukáže, kde optimalizovat.
  • Zvažte použití modifikátoru metod.
  • Používejte tabulky s předpočítanými hodnotami.
  • Omezte volání metod.
  • Snižte počet parametrů předávaných metodám.
  • Používejte porovnávání na nulu.
  • Používejte setClip() metodu pro omezení překreslované oblasti.
  • Snažte se vylepšit svůj algoritmus.
  • Používejte malé hodnoty konstant u příkazu switch.

Jak vidíte, i když je optimalizace někdy náročná, je to i zábava. Ale přesto neoptimalizujte kód hned od začátku. Nejprve se zaměřte na funkčnost a pak provádějte optimalizace. Je možné, že některé metody přepíšete i několikrát, ale výsledek bude určitě stát za to. Hodně zdaru při optimalizaci.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *