ugrás a tartalomhoz

"Karaktersorozatok sebessége" PHP-ben

Hojtsy Gábor · 2004. Ápr. 13. (K), 23.00
"Karaktersorozatok sebessége" PHP-ben
A PHP kétféle idézőjel használatát támogatja, és ráadásul a heredoc megadási módot is alkalmazhatjuk. Sok hiedelem kering azonban arról, hogy ezek közül egyik vagy másik gyorsabb, hatékonyabb eszköz lenne, és kizárólag azt szabad használnunk programjaink írása során. Cikkemben szeretném ezen hiedelmek egy részét megdönteni, valamint bemutatni, hogy egészen kis eszköztárral is igazolhatóak vagy cáfolhatóak az ilyen jellegű állítások, a PHP belső világának ismerete nélkül. Nézzük mire jutunk saját méréseinkkel, ha nekünk "bizonyíték kell, nem ígéret".

Három megadási mód

Minden gyakorlattal rendelkező PHP programozó tudja, hogy kétféle idézőjel használható PHP-ben karaktersorozatok megadására. Az egyiket - jobb elnevezés híján - dupla idézőjelnek ("), a másikat szimpla idézőjelnek, vagy aposztrófnak (') nevezzük. Az is széles körben ismert, hogy ezen két idézőjelbe zárási mód képességei között jelentős különbségek fedezhetőek fel, mégpedig hogy a dupla idézőjeles karaktersorozatokban változó helyettesítést és speciális karaktereket is alkalmazhatunk, míg aposztróffal zárt sztringek esetén ilyen nem lehetséges. A dupla idézőjelhez hasonlít a heredoc formában megadott karaktersorozat, hiszen a változók értékhelyettesítése itt is működik.

<?php
echo 'Ezt mindegy, hogy hogyan írjuk';
echo "Ezt mindegy, hogy hogyan írjuk";

echo "Itt azonban már egyáltalán nem mindegy\n";
echo 'Itt azonban már egyáltalán nem mindegy\n';

echo <<<VAS
A speciális karakterek itt is működnek.\nDe még mennyire.
VAS;
?>
Az első két példa ugyanazt jelenti, a második kettőből viszont csak az előbbi fog újsort kiírni a kimenetre, az utóbbi magát a \n karaktersorozatot jeleníti meg. A heredoc szabályai szerint a három kisebb jelet követően megadott karaktersorozat kerül a kimenetre, a speciális karakterek pedig a kettős idézőjelhez hasonlóan értelmezésre kerülnek.

A háromféle karaktersorozat megadási mód támogatása lehetővé teszi a programozók számára, hogy ne kelljen csúnyán festő megoldásokat választaniuk, ha egy karaktersorozatban a körbezárására használt idézőjelet szeretnék alkalmazni. Egyszerűen csak meg kell változtatni a körbezáró idézőjelet, vagy át kell térni a heredoc jelölésre.

A sebesség hiedelme

Sokáig igaz volt, hogy a kétféle idézőjellel megadott karaktersorozatok feldolgozása között "jelentős" sebesség különbség volt, azaz nagy mennyiségű dupla idézőjeles sztring használata esetén mérhetően lassabb futási időt kaptunk, mint aposztróf használatakor. Ez azonban a ma használatos PHP változatokra már nem igaz (vagy legalábbis teljesen elhanyagolható mértékben igaz), feltéve, hogy literális, speciális karakterektől mentes (azaz további feldolgozást nem igénylő) sztringekről beszélünk. Ugyanezen karaktersorozattal azonban meglepő lassúságot tapasztalhatunk heredoc jelölést használva.

Habár kiváló profiler programok állnak rendelkezésre a PHP programok gyenge pontjainak megtalálására, mégsincs speciális kiterjesztésekre szükségünk ahhoz, hogy egyszerűbb méréseket végezhessünk. Az idézőjelekkel kapcsolatos kijelentésekről magunk is meggyőződhetünk a következő egyszerű programmal:

<?php

echo "PHP " . phpversion() . "\n";

// Idő lekérdezésre használt függvény
function getmicrotime()
{
  list($usec, $sec) = explode(" ", microtime());
  return ((float) $usec + (float) $sec);
}

// ---------------------------------------------
$time_start = getmicrotime();
for ($i=0; $i < 100000; $i++) {
  $proba = "ez egy proba karaktersorozat";
}
$time = getmicrotime() - $time_start;
echo "Dupla idézőjelekkel:   $time\n";

// ---------------------------------------------
$time_start = getmicrotime();
for ($i=0; $i < 100000; $i++) {
  $proba = 'ez egy proba karaktersorozat';
}
$time = getmicrotime() - $time_start;
echo "Szimpla idézőjelekkel: $time\n";

// ---------------------------------------------
$time_start = getmicrotime();
for ($i=0; $i < 100000; $i++) {
  $proba = <<<VAS
ez egy proba karaktersorozat
VAS;
}
$time = getmicrotime() - $time_start;
echo "Heredoc megadással:    $time\n";
?>
A fenti kód nagyrésze a PHP microtime() függvényének dokumentációs oldaláról származik, tehát máskor is könnyen reprodukálható, ha esetleg nem is emlékeznénk a getmicrotime() kódjára. A microtime() eléggé finom időmérést tesz lehetővé, legalábbis ehhez a kísérlethez, tekintve, hogy százezerszer lefuttatjuk az értékadásokat. PHP 4.3.4-el és PHP 5.0.0RC1-el a következő kimeneteket kapom Linux alatt:

PHP 4.3.4
Dupla idézőjelekkel:   0.48893094062805
Szimpla idézőjelekkel: 0.49282789230347
Heredoc megadással:    1.1971590518951

PHP 5.0.0RC1
Dupla idézőjelekkel:   0.42129611968994
Szimpla idézőjelekkel: 0.42000198364258
Heredoc megadással:    1.0945689678192


Ez jól láthatóan azt jelenti, hogy a mérés alapján héttized százalékkal (azaz 0,7 százalékkal) gyorsabb a dupla idézőjelek feldolgozása PHP 4.3.4-ben, mint a szimpla idézőjeleké. Ilyen kis feladat esetén azonban nem lehet eltekinteni a futtatás körülményeitől. Különböző mérések eltérő eredményhez vezetnek, a program alapján minden reprodukálható. Tehát ezért nem érdemes áttérni egyik jelölésről a másikra. A heredoc típusú megadás viszont már látványosan lassabb. Azt is láthatjuk, hogy PHP 5.0.0-ban nem túl jelentős sebességváltozás észlelhető, de szerencsére jó irányban.

Mindig vannak kivételek

Legalább akkora hiba lenne most ökölszabálynak azt elfogadni, hogy a két idézőjel használata mindig ugyanarra az eredményre vezet, mint ha azt fogadnánk el, hogy valamelyik gyorsabb. A BlueShoes PHP kerektrendszer fejlesztői oldalán akadtam arra a tippre, miszerint érdemes megnézni a $ karakterek használatakor jelentkező sebesség különbséget.

Futtassuk tehát le újra a mérést úgy, hogy most az ez egy proba $ karaktersorozat $ sztringet helyezzük el a mérésekben, a dollár jelet csak a megtévesztés kedvéért használva. Az most nagyon fontos, hogy vagy a karaktersorozat vége, vagy szóköz szerepel a dollár után, hiszen különben változó hivatkozásnak tekintené a PHP. Nézzük így milyen eredményt kapunk:

PHP 4.3.4
Dupla idézőjelekkel:   1.4606549739838
Szimpla idézőjelekkel: 0.4861319065094
Heredoc megadással:    1.5480959415436

PHP 5.0.0RC1
Dupla idézőjelekkel:   1.1676690578461
Szimpla idézőjelekkel: 0.42343306541443
Heredoc megadással:    1.2074928283691


Mindjárt más. A PHP-nek eléggé sok időt el kell töltenie ebben az esetben azzal, hogy rájöjjön, hogy mi tulajdonképpen nem változót akartunk beilleszteni, hanem magát a dollár jelet, így pedig a dupla idézőjel közel a heredoc mellé szegődik sebességében. Persze ilyenkor körültekintő programozóként sokkal inkább azt kellene írnunk, hogy ez egy proba \$ karaktersorozat \$, hiszen ezzel jelezzük, hogy itt most nem változó-érték helyettesítésről van szó. Ezt a megoldást használva sikerül visszatérnünk arra a megállapításra, hogy a két idézőjel sebessége között elenyésző különbség van:

PHP 4.3.4
Dupla idézőjelekkel:   0.48207592964172
Szimpla idézőjelekkel: 0.48961210250854
Heredoc megadással:    1.5163249969482

PHP 5.0.0RC1
Dupla idézőjelekkel:   0.43033504486084
Szimpla idézőjelekkel: 0.42638397216797
Heredoc megadással:    1.166738986969

Változók helyettesítése

Nem érdemes azonban itt megállni, ha már úgyis benne vagyunk a mérésben, nézzük meg mi a különbség a változó helyettesítés és a karaktersorozat befűzés között. Megtartva a getmicrotime() függvényünket, írjuk át a ciklusokat a következőképpen:

<?php
$belso = "proba";

// ---------------------------------------------
$time_start = getmicrotime();
for ($i=0; $i < 100000; $i++) {
  $proba = "ez egy $belso karaktersorozat";
}
$time = getmicrotime() - $time_start;
echo "Dupla idézőjelekkel:   $time\n";

// ---------------------------------------------
$time_start = getmicrotime();
for ($i=0; $i < 100000; $i++) {
  $proba = 'ez egy ' . $belso . ' karaktersorozat';
}
$time = getmicrotime() - $time_start;
echo "Szimpla idézőjelekkel: $time\n";

// ---------------------------------------------
$time_start = getmicrotime();
for ($i=0; $i < 100000; $i++) {
  $proba = <<<VAS
ez egy $belso karaktersorozat
VAS;
}
$time = getmicrotime() - $time_start;
echo "Heredoc megadással:    $time\n";

?>
A proba szót a $belso változóval helyettesítettük, amit a dupla idézőjeleknél és a heredoc esetében használható változó helyettesítéssel, valamint az aposztrófoknál alkalmazható karaktersorozat összefűzéssel illesztettünk a helyére. Egy cikk keretében sajnos nem könnyű élni a hatásszünet eszközével és a dobpergés effekttel, ezért kénytelen vagyok rögtön elárulni az eredményt:

PHP 4.3.4
Dupla idézőjelekkel:   1.1817910671234
Szimpla idézőjelekkel: 0.80612206459045
Heredoc megadással:    1.2712509632111

PHP 5.0.0RC1
Dupla idézőjelekkel:   1.0963342189789
Szimpla idézőjelekkel: 0.6836302280426
Heredoc megadással:    1.0950348377228


Kisebb eltérések minden futtatásnál előfordulhatnak, de az arányokon ez nem változtat. Az eredmények azt mutatják, hogy ebben a teszt esetben a változó helyettesítés jelentősen több időbe telik, mint a karaktersorozat hozzáfűzése. Ez azzal magyarázható, hogy a változó nevet meg kell keresni a karaktersorozatban, mely ráadásul kellően bonyolult is lehet (objektum hivatkozással, tömb elemmel), majd pedig ezt helyettesíteni kell az értékével, és csak ezután folytatódhat a feldolgozás. Ebben az egyszerű tesztben hasonló eredményt kapunk, ha a PHP-nek segítünk a változó nevének megtalálásában, azaz ez egy {$belso} karaktersorozat kerül az idézőjelek közé, illetve a heredoc belsejébe. Érdekes összehasonlítás, hogy az első mérésünkhöz képest a PHP 4-es kicsit lassabban kezelte a heredoc karaktersorozatot, a PHP 5-ös azonban szinte ugyanazzal a sebeséggel. Ezzel egyenrangú partnerekké lépett elő a dupla idézőjel és a heredoc páros, hiszen PHP 5-ben elenyésző különbség figyelhető meg kettjük között.

Tanulság

Úgy gondolom, hogy mindenképpen fontos, hogy az olyan közhiedelmeket, melyekre napi munkánkat építjük magunk is próbáljuk ellenőrizni, legalábbis ha erre lehetőségünk van. Ebben az esetben egy valóban egyszerű mérési lehetőség adott, és mérésünk megnyugtató eredménye lehetővé teszi, hogy ne legyünk paranoiásak a dupla idézőjelekkel szemben, hanem igenis merjük használni, ha ez áll közelebb a megszokott stílusunkhoz. Ugyanakkor érdemes meggondolnunk, hogy változó helyettesítést vagy karaktersorozat összefűzést használunk egy nagyobb alkalmazásban, hiszen ezek sebessége jelentősebb eltérést mutat. Ennek kényszerű megválasztása is csak nagyon nagy alkalmazások esetén szükséges, különben sokkal fontosabb az olvashatósági szempontok figyelembevétele, hiszen hiába gyors a kódunk, ha nem átlátható, és később nehezen fejleszthető. Szintén fontos megjegyezni, hogy az idézőjelek cseréjével egy valós alkalmazásban érezhető sebességnövekedést nem fogunk elérni, hiszen valamilyen távoli szolgáltatás vagy adatbázis használata általában nagyságrendekkel nagyobb mértékben befolyásolja a sebességet. Ha ugyanazt az időt, amit idézőjeleink cseréjére fordítjuk, inkább az adatbázisunk optimalizásával töltjük el, sokkal hálásabbak lesznek látogatóink a gyorsabban működő oldalakat böngészve.

Azt is érdemes leszögezni a mérés eredményei alapján, hogy a PHP nemcsak hibajavításokkal és új szolgáltatásokkal jelenik meg verzióról verzióra, hanem az alapvető nyelvi konstrukciók is változnak, fejlődnek (sajnos időnként lassulnak is). Ezért egy általunk végzett mérés tanulságainak időtállóságában sem lehetünk biztosak, érdemes iőnként újra visszatérni kódolási módjainkat megalapozó elképzeléseink igazolására.
 
Hojtsy Gábor arcképe
Hojtsy Gábor
A Weblabor szerkesztője, mérnök informatikus. Több nemzetközi webfejlesztéssel és tartalomkezeléssel foglalkozó konferencián adott már elő, a hazai Web és Drupal konferenciák egyik szervezője. Szabadidejében legszívesebben amatőr színjátszással és énekléssel foglalkozik. Meggyőződése, hogy a pirospöttyös az igazi.
1

Javitas - bar lehet, hogy szorszalhasogatas ;)

Vaily · 2004. Ápr. 15. (Cs), 11.41
PHP 4.3.4
 Dupla idézőjelekkel:   0.48893094062805
 Szimpla idézőjelekkel: 0.49282789230347

Ez jól láthatóan azt jelenti, hogy héttized százalékkal (azaz 0,7
százalékkal) lassabb a dupla idézőjelek feldolgozása PHP 4.3.4-ben, mint a
szimpla idézőjeleké. Tehát ezért nem érdemes áttérni az aposztrófokra.

---

Szerintem itt gyorsabb ennyivel, nem lassabb. Bar lehet, hogy tobbszor
lefuttatva lehetne lassabb is... :)
4

Teljesen igaz

Hojtsy Gábor · 2004. Ápr. 16. (P), 21.01
Minden tekintetben igaz, amit írsz, a cikk szövege helytelen, és többször futtatva ugyanezt az ellenőrzést teljesen más eredmények jönnek ki (arányokat illetően és a nyertes pozíciót illetően is). Ennyire kis feladat esetén nem lehet a futási környezetbeni körülményeket figyelmen kívül hagyni. Eszerint módosítottam a cikket.
2

teszt mashol

Anonymous · 2004. Ápr. 16. (P), 20.39
En a "Változók helyettesítése" teszt rutint futtattam le tobbszor, es amint latszik van kulonbseg, de a fenti teszthez kepest nem 46%-al, hanem csak ~40%-al lassabb a dupla idezojeles
megoldas... (CELERON P4 1,7G)

PHP 4.3.1
Dupla idézőjelekkel: 0.32194399833679
Szimpla idézőjelekkel: 0.23652577400208
Heredoc megadással: 0.38350009918213

Dupla idézőjelekkel: 0.31602478027344
Szimpla idézőjelekkel: 0.22558093070984
Heredoc megadással: 0.31760406494141

Dupla idézőjelekkel: 0.31773710250854
Szimpla idézőjelekkel: 0.22830104827881
Heredoc megadással: 0.33787488937378

Udv: Attila
3

Verziófüggő

Hojtsy Gábor · 2004. Ápr. 16. (P), 20.47
A sebességkülönbség jelentősen függhet a PHP verziótól, sőt ez akár meg is fordulhat. Mindenféle optimalizálást végeznek ugyanis, ráadásul időnként betesznek új szolgáltatásokat, amik megváltoztatják a mérhető sebességet.
5

Csak a valtozatossag kedveert...

Anonymous · 2004. Ápr. 28. (Sze), 23.01
PHP 4.3.3
Dupla idézojelekkel: 0.283003091812
Szimpla idézojelekkel: 0.118773937225
Heredoc megadással: 0.28635597229
6

Milyen gépen?

Bártházi András · 2004. Ápr. 29. (Cs), 06.45
Hozzátenném, hátha valakinek nem egyértelmű, hogy nem szabad összehasonlítani egymással a különböző személyektől itt megjelent számokat, mert különböző gépeken végezték a méréseket. Amit lehet, az egyes méréseken belül megnézni, hogy mit tud a PHP...

-boogie-
7

Laptop p3[1ghz] 512 ram

durexhop · 2005. Jan. 26. (Sze), 23.44
PHP 5.0.3

Valtozo $proba = "ez $belso...";

Dupla idézőjelekkel: 0.709594011307
Szimpla idézőjelekkel: 0.28307890892
Heredoc megadással: 0.738415956497
8

Elnevezés

laji · 2006. Már. 13. (H), 15.15
Az egyiket - jobb elnevezés híján - dupla idézőjelnek ("), a másikat szimpla idézőjelnek, vagy aposztrófnak (') nevezzük.

Tudtommal (legalábbis nyelvésztől olvastam) az egyik (a kettős) az idézőjel, a másik a hiányjel (nyelvtani - nem keverendő össze a számtanival).
9

attól függ

Őry Máté · 2007. Aug. 8. (Sze), 13.27
a magyar tipográfia ismeri a harmadlagos idézőjelként használt ’félidézőjel’ fogalmát is, amely formáját tekintve megegyezik a hiányjellel. ez utóbbi pedig a szintén helyes görög eredetű aposztróf szó magyar változata.