Zvyšte rychlost vašeho JS kódu

Pokud je pro vás rychlost kódu důležitá, neměl by vás tento článek minout. Je možné, že objevíte něco, co můžete ještě zlepšit.

Rychlost kódu je důležitá. Čím rychlejší ten váš bude, tím víc budou vaši uživatelé spokojeni. Cílem tohoto článku není ukázat pokročilé techniky optimalizace v rozsáhlých aplikacích. Spíše předvedu nejrychlejší varianty jednoduchých operací, které se ve vašem kódu běžně vyskytují.

Důležité je však také říci, že ne vždy by měla mít rychlost přednost před čitelností a jasností. Některé zde ukázané techniky mají jednu nevýhodu – ne všem je hned jasné, co dělají a k čemu jsou. Proto před jejich použitím nejdříve zvažte, zda mají opravdu smysl.

A ještě jedna poznámka. Tento článek není o frázích typu “nepoužívejte konstrukci with”, “eval je zlo” nebo “vyhněte se try-catch-finally”. Místo toho vám řeknu, co používat, a ne co nepoužívat. Jdeme na to.

Porovnávání

JavaScript nabízí dvě možnosti porovnávání – trojité rovnítko (===) a klasické dvojité (==) a samozřejmě jejich ekvivalenty v nerovnosti. Určitě víte, jaký je mezi nimi rozdíl a určitě jste slyšeli, že se doporučuje používat to trojité.

V některých případech je použití dvojitého opodstatněné. Například když potřebujete, aby se "2" rovnalo 2. A to je právě ta největší slabina dvojitého rovnítka. Pokud jsou totiž operandy různého datového typu, tato operace provede nejdříve jejich konverzi. A to trvá.

Oproti tomu rovnítko trojité nic takového nedělá. Prostě jen porovná hodnoty. Žádné přetypovávání neprovádí. Obě varianty porovnání dosahují stejných rychlostních výsledků, když jsou operandy stejného datového typu. Pokud se však typy liší, nastane určité zhoršení výkonu při porovnávání pomocí ==.

Nabízím samozřejmě důkaz. A proto, používejte === všude, kde není dvojité rovnítko nutností.

Přetypovávání na bool

Určitě jste se již setkali s podobným výrazem jako !variable, a pravděpodobně i s !!variable. Je to klasická operace negace, tudíž z každé pravdivé hodnoty (každé číslo kromě nuly, neprázdný řetězec, atd.) udělá false, v opačném případě true. Varianta s dvěma vykřičníky pouze překlápí znegovanou hodnotu na opačnou.

A hned se dostáváme k oné čitelnosti. Tomu, kdo se s JavaScriptem nesetkává každý den, může !!variable na moment lehce zamotat hlavu. Obecně se k přetypovávání na bool doporučuje spíše Boolean(variable) (pozor, ne new Boolean(variable)!), kvůli tomu, že je hned jasné, co se zde děje. Avšak varianta s dvěma vykřičníky je znatelně rychlejší.

Dělení mocninami dvou

Jak často dělíte dvěma? Pokud mnohokrát, věděli jste, že stejného výsledku můžete docílit pomocí bitového posunu vpravo? A nejenom dvěma, ale všemi mocninami čísla dvě. Takže 16 >> 1 je to samé jako 16 / 2, až na to, že první varianta je rychlejší. Stejně tak to platí pro 16 >> 2 (16 / 4), 16 >> 3 (16 / 8), a podobně.

Opět předvádím důkaz mého tvrzení. Zajímavostí je, že podobnou techniku lze využít také při násobení pomocí bitového posunu vlevo (3 << 1 je stejné jako 3 * 2), ale z měření jsem zjistil, že zde není žádný výkonnostní rozdíl. JavaScript nejspíše při násobení provádí nějaké optimalizace. Takže můžete bitový posun používat jako machrovinku u obou operací, nicméně užitek to přinese pouze při dělení.

Převod na celá čísla

Pro převod řetězců nebo desetinných čísel na celá čísla nejspíše používáte funkci parseInt('123', 10). Je to tak vlastně správně (pozor však na určité záludnosti parseInt) a každý si je schopen odvodit, co daný kus kódu provádí. Nicméně existují i rychlejší varianty.

A zde se poprvé rozcházíme v prohlížečích. Zatímco v Chromu a Opeře jednoznačně válcuje konkurenci metoda Math.floor(value), ve Firefoxu dosahují nejvyššího výkonu bitové operace negace, součtu a posunu a skoro stejně je to v IE11, kde jen zaostává bitová negace. Aby toho nebylo málo, v Safari zas nejlépe vychází tradiční parseInt. Sami se můžete podívat.

Takže se nám v podstatě nabízí dvě varianty – Math.floor(value) nebo value | 0. Podíl prohlížečů, kde je jedno nebo druhé rychlejší, je víceméně stejný, a tak nám ani toto hledisko nepomůže v rozhodování. Je to tedy čistě dle vašeho uvážení.

Konverze na řetězec

Stejně jako při převodu na čísla nebo boolean, i konverze na řetězec má svojí funkci, a sice String(value). Tento způsob však není téměř nikde nejrychlejší. Co se týče Chromu, tak tam jasně vede spojení s prázdným řetězcem pomocí operátoru plus ('' + value). Dlouho to tak platilo i ve Firefoxu, ale v nových verzích kraluje metoda value.toString(). Co se týče ostatních prohlížečů, vedení '' + value převládá, proto asi mohu doporučit tento způsob. Zde je benchmark.

Dobrá zpráva je, že řešení s plusem funguje i s objekty. Co tím myslím je fakt, že daný objekt může mít vlastní implementaci metody toString(), která může sloužit například k serializaci objektu. Naštěstí spojení s prázdným řetězcem vrátí ten samý výsledek, který by vrátilo přímé volání obj.toString(). Nabízím jsfiddle, který to potvrzuje.

Procházení pole

Zapomeňte na funkcionální způsob arr.forEach(callback) nebo starší for (var i in arr). Ty patří k tomu nejpomalejšímu, co můžete vůbec použít. Místo toho pole procházejte tradičním zápisem for smyčky, pouze si uložte velikost pole předem, ať při každé iteraci nepřistupujete k vlastnosti length. Ovšem tohle pro vás určitě není žádná převratná novinka.

Zde se můžete podívat, jak forEach a for...in zaostávají za konkurencí. Klíčové slovo in je ve všech svých použití pomalé, proto ho ani nepoužívejte k zjišťování přítomnosti vlastností v objektu ('prop' in obj) a místo toho použijte klasické hranaté závorky (obj['prop'] !== undefined) nebo tečkovou syntaxi (obj.prop !== undefined). Přidám i důkaz.

Konkatenace polí

Určitě jste někdy potřebovali spojit dvě pole do jednoho. Po přečtení API k JavaScriptovým polím jste objevili metodu arr1.concat(arr2). Opět, každý ihned pochopí, k čemu je daný kus kódu určen. Nicméně existuje mnohem rychlejší způsob, jak spojit dvě pole, a sice pomocí konstrukce Array.prototype.push.apply(arr1, arr2).

Tento benchmark to potvrzuje. Pokud budete ve svém kódu tento způsob používat vícekrát, vyplatí se uložit si Array.prototype.push do proměnné, ale je to spíše pro pohodlí, zrychlení není nijak závratné.

Procházení objektem

Jak jsme si již řekli, operátor in není příliš šťastné používat. Jak tedy iterovat přes objekty, kde hodnoty nejsou uloženy na indexu, ale podle určitého klíče? Od ECMAScript 5.1 máme k dispozici Object.keys(obj), která vrací pole klíčů daného objektu. Bohužel tato metoda chybí ve starších verzích IE, konkrétně do verze 9. Pokud však nemáte v plánu tyto prohlížeče podporovat, nejrychlejší způsob, jak projít objekt, je tento:

for (var i = 0, keys = Object.keys(obj), len = keys.length; i < len; i++) {
    obj[keys[i]];
}

Závěr

Pokud ve vašem krátkém skriptu změníte dvojité rovnítko na trojité, nečekejte žádné rapidní zrychlení. Avšak pokud již máte rozsáhlejší kód a aplikujete v něm všechny zmíněné optimalizace, zvýšení rychlosti již může být znát. A není to žádná hardcore teorie, pouze malé změny v běžných technikách. Zapamatovat si je není tak obtížné.

Jak jsem zmínil na začátku, optimalizace za každou cenu můžou velmi snížit čitelnost vašeho kódu. Také by se měly dělat až na hotovém kódu, ne při jeho psaní. Pokud znáte podobná vylepšení, určitě budu rád, když se s nimi nějakým způsobem podělíte, ať jsme zas o něco chytřejší.

Klidný, nekonfliktní a skromný člověk, vždy s úsměvem na tváři. Vývojář, co se snaží prorazit do velkého světa vývoje softwaru. V tuto chvíli se nejraději topí v bažinách JavaScriptu a dalších webových technologií. Miluje čerstvé nápady, liberální řešení a minimalismus.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 17

Přehled komentářů

Naith Jaký je smysl první rady?
Petr Nevyhoštěný Re: Jaký je smysl první rady?
honzamarek88 mikrooptimalizace
Petr Nevyhoštěný Re: mikrooptimalizace
karfcz Re: mikrooptimalizace
KarelI Re: mikrooptimalizace
Jirka Kosek Předčasná optimalizace
Petr Nevyhoštěný Re: Předčasná optimalizace
Daniel Steigerwald
Ladislav Thon Re:
Michal Čaplygin K procházení polí
e
Radek_CZ
e Re:
Radek_CZ Re:
e Re:
Radek_CZ Re:
Zdroj: https://www.zdrojak.cz/?p=12328