PHP: Ettevaatust “global”-iga

Jaanus Putting
RSS: Jaga:

Käesolevat postitust ajendas kirjutama üks elementaarseid reegleid PHP-s
(global $a != $GLOBALS[‘a’]) ning selle vastu eksimine, mis siis maksab lõpuks meeletu kiirusekaotusena kätte.

Oletame, et meil on lihtne skript, kus:

  1. koostatakse mõnekümnest tuhandest elemendist koosnev massiiv $a
  2. käib for tsükkel paar tuhat korda ning igal ringil kutsub välja funktsiooni f()
  3. funktsioonis f() võetakse count() abil massiivi $a pikkus

$a = Array ();
for ($c = 0; $c < 20000; $c++) {
$a[] = $c;
}

function f () {
global $a;
$cnt = count ($a);
}

for ($c = 0; $c < 2000; $c++) {
f ();
}

Selline skript käib minu testmasina peal… humm… 17 sekundit. Suhteliselt kaua, kui arvestada, et kaks tsüklit kokku käivad vaid 22000 korda.

Asja uurides jõudsin järeldusele, et count() käitub veidralt, kuna massiivi pikkuse võtmine peaks count() jaoks olema ju vaid ühe täisarvu lugemine mälust.
$cnt = count ($a) eemaldamisega võttis skript aega 0.000… sekundit.

Mis seal salata, olin lõpuks otsapidi php.net-i bugtrackis, kust leidsin siis eelmise sama orgi otsa lennanud tegelase postituse.

Probleem algab nimelt sellest, et global loob funktsiooni skoobis viida (reference) nimetatud globaalse muutujani. Täpselt samasuguse viida nagu siis, kui anda funktsioonile massiiv pass-by-reference parameetri kaudu edasi: function f (&$a) {…}

Viitade süsteem PHP-s on aga aegade algusest peale vildakas, poolik ja üleüldse !#!#¤%!$?# olnud, mistõttu pole eriti imestada, et kui count()-ile anda parameetriks viit, siis esiteks teeb PHP sellest muutujast külma rahuga mällu teise koopia ning alles seejärel antakse koopia edasi count()-ile. Sama kehtib lisaks count()-ile ka kõikide teiste funktsioonide kohta, mis selgesõnaliselt viita parameetrina ei aktsepteeri.

Kui nüüd nende uute kuid kurbade teadmistega selle õnnetu funktsiooni f() sisse vaadata, siis teame me seda, et 17 sekundi sees allokeeriti mälu 2000 korda, kopeeriti 20000 elemendilist massiivi 2000 korda ning vabastati mälu 2000 korda. Pisut rohkem sebimist kui lihtsalt 2000 korda täisarvu lugeda mälust?

Aga lahendus ise on lihtne, olenevalt olukorrast peaks:

  • kasutama $GLOBALS superglobals-it

function f () {
$cnt = count ($GLOBALS[‘a’]);
}

  • või andma selle massiivi $a lihtsalt parameetrina funktsioonile edasi.
    Irooniline siinkohal on see, et nüüd on PHP küll piisavalt “tark” teadmaks, et pass-by-value parameetrist pole vaja koopiat teha, kui selle väärtust funktsiooni siseselt ei muudeta.

function f ($a) {
$cnt = count ($a);
}

Skript käib nüüd vaid 0.04s

Populaarsed postitused

Kuidas Helikuju.ee esimene koduleht sai valmis vähem kui tööpäevaga

Helikuju.ee koduleht valmis välgukiirusel

Lemme Suve
Kui oled ettevõtja või loomeinimene, kes on kodulehe tegemist pikalt edasi lükanud, siis see lugu on just sulle. Oma kogemust jagab helidisaini agentuur...
Produktize.eu:  kuidas ehitada rahvusvahelist usaldusväärsust ja kasvatada turgu .EU domeeniga

Produktize.eu: kuidas ehitada rahvusvahelist usaldusväärsust ja kasvatada turgu .EU domeeniga

Nikita Tikhomirov
Eesti ärimaastikul tõusevad esile ettevõtted, kes ei piirdu vaid kohaliku turu teenindamisega, vaid mõtlevad suuremalt – sihtturgudele, brändi kuvandile...
Partner soovitab: Kuidas luua koduleht, mis kõnetab ideaalset klienti

Partner soovitab: Kuidas luua koduleht, mis kõnetab ideaalset klienti

Cathy Kask, Celeht.ee
Kas oled vahel tundnud, et su koduleht ei peegelda sind ega sinu ettevõtet? Nii palju on öelda, nii palju võimalusi – kuid lõpuks jääd siiski omaenda...
Xdebug seadistamine Zone veebimajutuses: PhpStorm

Xdebug seadistamine Zone veebimajutuses: PhpStorm

Arne Meier
Varasemalt on Zone blogis olnud juttu Xdebug kasutamisest nii Sublime Texti kui ka Visual Studio Code baasil. Kui eelmised näited käsitlevad seadistamist...