PHP ciklų greitaveikos optimizavimas

Programavimas | PHP

04175 • atnaujinta prieš:

Pav.: PHP ciklų greitaveikos optimizavimas

Visi, kas daugiau ar mažiau moka programuoti, yra susipažinę su kintamaisiais, sąlygos sakiniais ir ciklais. To dažniausiai mokoma per pirmąsias pamokas. Ciklų naudojimas yra savaime suprantamas ir neišvengiamas, o kokius naudoti kiekvienas renkasi pagal patikimą, naudojimo, užrašymo patogumą, tačiau retas susimąsto dėl ciklų veikimo greičių.

Tikriausiai dažniausiai pasitaikantis ciklas yra FOR – aiškus, paprastas, užrašomas panašiai beveik visose programavimo kalbose: pradinė sąlyga, pabaigos sąlyga ir kitimo žingsnis.

PHP kalboje naudojami ir kiti ciklo sakiniai: FOREACH, WHILE, DO WHILE.

Internete rastas angliškas straipsnis apie javascript ciklų greičio optimizavimą (http://www.webreference.com/programming/optimize/speedup/chap10/3/2.html) užvedė ant minties išsiaiškinti tiesą ir apie PHP.

Labai dažnas internete randamas FOR ciklo užrašymas atrodo taip:

for ($i = 0; $i < count($array); $i++) {..}

Ar kada nors susimąstėte kaip veikia šis ciklas? Jis suskaičiuoja nuo 0 iki tiek kiek elementų yra masyve $array, tačiau kaskart tikrinant sąlygą $i < count($array) yra vykdomas masyvo elementų perskaičiavimas.

Taigi, pasiruošiame pradinius duomenis:

// užpildome masyvą $a 100000 elementų, kurių kiekvienas lygus 5
$a = array_fill(0, 100000, 5);

Visuose cikluose tiesiog suskaičiuosime visų masyvo elementų sumą.

Pirmas bandymas – dažnai sutinkamas ciklo sakinys

$s = 0;
for($i=0; $i < count($a);$i++) {
  $s += $a[$i];
}

Antras bandymas – optimizuotas prieš tai buvęs ciklas

$s = 0;
$length = count($a);
for($i=0; $i < $length; $i++) {
  $s += $a[$i];
}

Trečias bandymas – jei panaršysite po gudresnių programuotojų kodą, antrą bandymą galima užrašyti ir taip

$s = 0;
for($i=0, $length=count($a); $i < $length; $i++) {
  $s += $a[$i];
}

Ketvirtas bandymas – anot aukščiau paminėto straipsnio, skaičiavimas atbuline tvarka veikia greičiau nei skaičiavimas aukštyn, pamėginkime

$s = 0;
$length = count($a);
for ($i = $length-1; $i >= 0; $i--) {
  $s += $a[$i];
}

Penktas bandymas – dar paoptimizuokime ketvirtą bandymą, nes anot to paties straipsnio --$i veikia greičiau nei $i--

$s = 0;
$length = count($a);
for ($i = $length; $i > 0;) {
  $s += $a[--$i];
}

Atkreipkite dėmesį, jog ketvirtame ir penktame bandymuose skaičiavimas atbulinis, todėl norėdami atspausdinti elementus nuo 0 iki $length, gausite įterpti indekso perskaičiavimo operaciją, kuri tikrai pablogins laiką.

Šeštas bandymas – dėl įdomumo, neužmirškime ir pabandykime FOREACH

$s = 0;
foreach($a as $key => $value) {
  $s += $value;
}

Septintas bandymas – anot straipsnio, do {} while veikia greičiausiai, bandom, tačiau dėl objektyvumo ir dėl DO sakinio specifikos turime įdėti pradinę IF sąlygą, ir pasitikrinti ar masyvas netuščias

$s = 0;
$i = 0;
$length = count($a);
if ($length > 0) do 
{
  $s += $a[$i];
  $i++;
}
while ($i < $length);

Aštuntas bandymas – bandom ar tiesa, jog ++$i veikia greičiau už $i++, optimizuojam septintą bandymą

$s = 0;
$i = 0;
$length = count($a);
if ($length > 0) do 
{
  $s += $a[$i];
  ++$i;
}
while ($i < $length);

Devintas bandymas – skaičiavimas žemyn dar greičiau.. bandom

$s = 0;
$i = count($a);
if ($i > 0) do 
{
  $i--;
  $s += $a[$i];
}
while ($i);

Dešimtas bandymas – taigi --$i veikia greičiau nei $i--, tikrinam

$s = 0;
$i = count($a);
if ($i > 0) do 
{
  --$i;
  $s += $a[$i];
}
while ($i);

Atkreipkite dėmesį, jog devintame ir dešimtame bandymuose skaičiavimas atbulinis, todėl norėdami atspausdinti elementus nuo 0 iki count($a), gausite įterpti indekso perskaičiavimo operaciją, kuri tikrai pablogins laiką. Netikite?

Vienuoliktas bandymas - netikintiems

$s = 0;
$length = $i = count($a);
if ($i>0) do 
{
  $s += $a[$length - $i];
  --$i;
}
while ($i);

Rezultatai:

Bandymas Windows Ubuntu
Pirmas bandymas 0.040683031082153 0.0350840886434
Antras bandymas 0.0119948387146 0.011077245076467
Trečias bandymas 0.012179851531982 0.0110880533854
Ketvirtas bandymas 0.011506080627441 0.0127126375834
Penktas bandymas 0.0093190670013428 0.010710716247533
Šeštas bandymas 0.0092620849609375 0.011339982350667
Septintas bandymas 0.0098981857299805 0.0104285875956
Aštuntas bandymas 0.008842945098877 0.009970664978
Devintas bandymas 0.0083010196685791 0.0087486902872667
Dešimtas bandymas 0.0075099468231201 0.0086693763732667
Vienuoliktas bandymas 0.0087618827819824 0.0097419420878

Išvados

Dešimtas ciklas veikia greičiausiai, tačiau padarius indekso perskaičiavimą, sulėtėja, bet vis vien rodo geriausius rezultatus. Jei nepatinka atgaliniai skaičiavimai ir papildomi kintamieji, naudokite aštuntą ciklą.

Niekada nenaudokite pirmojo, nes jis veikia ~3 kartus lėčiau nei kiti FOR ar FOREACH ir ~4 kartus lėčiau nei DO {} WHILE

Pamoka pateikta

Facebook