Optimalizace kódu

Školení, která pořádám

Už jsem četl několik článků o optimalizaci PHP kódu, které popisují, že jeden způsob zápisu stejného kódu je rychlejší než druhý. Článek většinou obsahuje skript, který na začátku změří funkcí microtime čas, pak opakovaně provede nějaký kód, na konci opět změří čas a na základě toho určí nejrychlejší metodu. Ti pečlivější kód spustí několikrát, aby se vyhnuli možným výchylkám způsobeným vnějšími okolnostmi, ti se znalostmi vnitřností PHP zrychlení i vysvětlí.

Jestli teď čekáte, že představím několik zaručených optimalizačních triků, tak vás asi zklamu. Proč? Např. WeberBlog objevně popisuje fantastické zrychlení 0.15 s při 2.000.000 průchodech cyklu, což může mít ve vytížených aplikacích prý velký vliv. Můžete si snadno vypočítat, že na webu s milionem shlédnutých stránek měsíčně a dvou cyklech na stránku to je úspora 0.0000058% času – to se vážně moc nevyplatí, pokud je s tím spojená menší přehlednost kódu. Těžiště zrychlení je zkrátka v něčem jiném než v tom, jaký alternativní zápis použít. Rychlý kód dělají správné datové struktury a algoritmy, správná konfigurace, případně pokročilejší techniky jako cachování nebo rozkládání zátěže.

Jaké jsou další zaručené triky? Ilia Alshanetsky popisuje pomalost funkce strlen a doporučuje ji nahradit za !isset($foo[5]), $i++ je také zbytečně pomalé, každý ví, že print je významně pomalejší než echo a že pro výpis dlouhých textů je lepší vystoupit z PHP kódu. Stejný autor také doporučuje používat include "./file.php" místo include "file.php". Já bych to spíš doporučil kvůli zamezení kolizí se soubory v ostatních adresářích include_path. Jinde se můžete dočíst, že apostrofy jsou rychlejší než uvozovky, include je mnohem rychlejší při používání absolutních cest, switch je rychlejší než několik elseif a str_replace je rychlejší než strtr.

Možná byste se také někde dozvěděli (nebo si to můžete změřit), jestli je rychlejší "ahoj $a", "ahoj {$a}" nebo 'ahoj ' . $a nebo jaká je nejrychlejší možnost z !$var, !strlen, a empty (byť všechno znamená něco jiného).

Určitě nechci nikoho nabádat k záměrnému používání pomalejších konstrukcí. Čitelnost kódu by ale vždy měla mít přednost. Pokud také máte pocit, že vaše aplikace běží pomalu, nesnažte se např. přepsat všechny uvozovky na apostrofy, žádného zrychlení tím nedosáhnete.

Jak tedy vytvořit rychlejší kód? Pište efektivní algoritmy, navrhněte správně datové struktury, používejte indexy v databázi, na jednoduchou kontrolu řetězce nepoužívejte v dlouhých cyklech regulární výrazy, ale např. funkce pro práci s řetězci nebo třeba funkci is_numeric. Pokud regulární výrazy použijete, vyhněte se Posixovým a použijte Perlové, které jsou podstatně rychlejší a navíc silnější. Pokud existuje vestavěná funkce řešící nějaký problém (např. pro převod kódování znaků), tak ji použijte a nepište vlastní – v podstatě vždy bude ta uživatelská pomalejší. Použít můžete i různé cachování, ať už na úrovni HTTP, PHP kódu (APC, eAccelerator, Zend Optimizer) nebo jeho výstupu.

Neméně důležitá je také správná kompilace PHP, konfigurace a nastavení okolního prostředí. Kvalitní informace jsou k dispozici např. v Přednáškách tvůrců PHP.

Přijďte si o tomto tématu popovídat na školení Výkonnost webových aplikací.

Jakub Vrána, Dobře míněné rady, 17.8.2005, diskuse: 13 (nové: 0)

Diskuse

ikona dgx:

Myslím, že naopak strtr je rychlejší než str_replace, protože prochází pole jen jednou. Každopádně každá funkce pracuje trošku jinak a nelze je automaticky zaměňovat.

Ale jinak souhlasým, že jeden chybějící index dokáže víc zpomalit aplikaci, než milión dvojitých uvozovek prokládaných printy ;)

ikona Jakub Vrána OpenID:

Myšleno bylo toto:

<?php
str_replace
(array("a", "b"), array("c", "d"), "abcd");
strtr("abcd", array("a" => "c", "b" => "d"));
strtr("abcd", "ab", "cd"); // případně
?>

V tomto případě str_replace řetězec prochází také jenom jednou a podle Ilii by měl být rychlejší.

ikona dgx:

Není to pravda - str_replace bude řetězec procházet dvakrát. Stačí lehká modifikace a můžeš si to ověřit:

<?php
// vrátí dbdd
str_replace(array("a", "c"), array("c", "d"), "abcd");

// vrátí cbdd
$b = strtr("abcd", array("a" => "c", "c" => "d"));
?>

Takže strtr je prakticky vždy rychlejší.

ikona Jakub Vrána OpenID:

Máš úplnou pravdu, kontroloval jsem to i ve zdrojácích. str_replace() pravděpodobně bude rychlejší pouze v některých speciálních případech (např. krátký prohledávaný řetězec).

ikona Jakub Vrána OpenID:

http://ilia.ws/archives/73-PHPWorks-Perfor…-Slides.html, slide 32:

<?php
$src_str
= file_get_contents("some_big_file");

$src = array('abc', 123, 'text');
$dst = array('cba', 321, 'txet');

$s = microtime(1);
for (
$i = 0; $i < 10000; $i++)
        str_replace($src, $dst, $src_str);
$e = microtime(1);
echo (
$e - $s) . "\n"; // 5.69 seconds

$new_rep = array_combine($src, $dst);

$s = microtime(1);
for (
$i = 0; $i < 10000; $i++)
        strtr($src_str, $new_rep);
$e = microtime(1);
echo (
$e - $s) . "\n"; // 54.42 seconds
?>

Prodávám, jak jsem koupil.

lukas:

a co je lepsi zhlediska vykonu, efektivnosti, prehlednosti, prasarnosti

printf('<li><a href="index.php?var=%s">%s</a></li>', $row['id'], $row['name']);

nebo

<li><a href="index.php?var=<?php echo $row['id'];
?>"><?php echo $row['name']; ?></a></li>

jakub:

prvni je podle me cele "spatne" ...
echo je rychlejsi nez printf a je podle me lepsi psat php do html a ne generovat html pomoci php minimalne z duvodu prehlednosti ... protoze kdyz mate html a v nem php editor vam vse krasne zvyrazni :)

ikona spaze:

OTOH, prvni je mnohem prehlednejsi.

martinpav:

Lenze pomocou printf/sprintf si velmi lahko mozete  vytvorit jednoduche "sablony" ktore mozete mat pekne pokope. Koli zmene potom vobec nemusite liezt do kodu. Nehladiac na to ze mozte zmenit format zobrazovania bez upravy kodu.

tomáš_M:

Ideální je oddělit HTML od PHP úplně (smarty a další) - nevím co výkon, ale přehlednost je bez debat nejlepší

Anonym:

Som to trochu skusal a:
<?php
   
echo "<span style='display: none;'>";
    $repeat    = 1000000;
    $speed1    = 0;
    $speed2    = 0;
    $keys    = array();
    $s = microtime(1);
    for($i = 0; $i < $repeat; $i++):?>a<?php endfor;
    $e = microtime(1);
    $speed1 = ($e - $s);

    $s = microtime(1);
    for($i = 0; $i < $repeat; $i++){
        echo "a";
    }
    $e = microtime(1);
    $speed2 = ($e - $s);

    echo "</span>
    Speed 1:
$speed1<br />
    Speed 2:
$speed2";
?>
Vysledok:
Speed 1: 1.87455105782
Speed 2: 1.68907189369

Taze je zbytocne vychadzat z php codu a pisat to v html, len preto, ze ti to editor zvirazni! Ja pomaly prestavam kodovat html a pisem uz len cisto php, ale nikdy mi nevadilo, ze mi to editor nezvyrazni.

A to nehovorim, ze html sa zvykne vypisovat takto:for($i = 0; $i < $repeat; $i++):
?>
a
<?
endfor; //a to je potom o 2 sekundy dlksie.

A opravte ma ked sa milim, ale server prechadza kod postupne, narazi na php tak si zavola parser, ten poziadavku spracuje a vrati html (mozno som tresol kktinu, ale nekde som to videl napisane). A cim viac poziadaviek, tym pomalsie to ide. Takze ja som zato aby sa php nepremiesavalo s html.

ikona Jakub Vrána OpenID:

Přečti si článek ještě jednou. Rychlost není vše, přehlednost kódu je leckdy mnohem důležitější. Kolik stojí MHz výkonu procesoru a kolik stojí vteřina času programátora? (Navíc s přihlédnutím k tomu, že cena prvního jde neustále dolů, kdyžto cena druhého zase nahoru.)

Navíc efektivní kód skutečně nedělají takovéto nanosekundové optimalizace.

BoneFlute:

"A opravte ma ked sa milim, ale server prechadza kod postupne, narazi na php tak si zavola parser, ten poziadavku spracuje a vrati html"
Opravím :-) Server kod neprochází. Pokud má soubor mime php, tak se zavolá parser. Ten postupně vykoná všechny konstrukce <?php .. ?>. Vše ostatní tam prdne, jak je.

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.