Srovnání nahrazovacích funkcí

Minulý článek mne namotivoval, abych si ve své malé laboratoři uspořádal miniolympiádu nahrazovacích funkcí. Pojďme na to.

Soutěžící

  • str_replace
  • ereg_replace
  • preg_replace

Disciplíny

  • náhrada na začátku řetězce
  • mnohonásobná náhrada
  • extrakce

Pravidla

Každý soutěžící bude mít 100 pokusů na jednu disciplínu. Úkolem každé disiplíny bude 10 000x provést zvolený typ náhrady. Hodnotící kritéria budou dvě:

  • počet dotazů za sekundu měřený Apache Benchem (request per second - RPS)
  • průměrovaný čas měřený přímo v PHP skriptu (čas v ms)

Náhrada na začátku řetězce

V této disciplíně musela funkci str_replace pomáhat její kamarádka strpos.

if (strpos($text, 'http://') === 0) str_replace('http://', '', $text);
preg_replace('%^http://%', '', $text);
ereg_replace('^http://', '', $text);

Závodník RPS čas
strpos + str_replace 15 105
preg_replace 25 71
ereg_replace 14 127

Na zkoušku jsme pak ještě nechali poslední dva závodníky provést před náhradou expanzi:

preg_replace('%^http://(.*)$%', '$1', $text);
ereg_replace('^http://(.*)$', '$1', $text);

Závodník RPS čas
preg_replace 22 87
ereg_replace 1,14 1227

Mnohonásobná náhrada

str_replace('abc', 'cba', $text);
preg_replace('%abc%', 'cba', $text);
ereg_replace('abc', 'cba', $text);

Závodník RPS čas
str_replace 32 60
preg_replace 22 88
ereg_replace 11 171

Extrakce

str_replace('" />', '', str_replace('<img src="', '', $text));
preg_replace('%^<img src="([^"]+)" />$%', '$1', $text);
ereg_replace('^<img src="([^"]+)" />$', '1', $text);

Závodník RPS čas
str_replace 19 102
preg_replace 24 78
ereg_replace 3,65 527

Po přidání volitelných mezer

preg_replace('%^<img +src="([^"]+)" +/>$%', '$1', $text);
ereg_replace('^<img +src="([^"]+)" +/>$', '1', $text);
nám ereg zpomaluje až k 800ms, zatímco preg zůstává na hezkých 80ms.

Vyhlášení vítězů

Výsledky asi nebudou pro znalého diváka žádným překvapením.

Str_replace obhájila své prvenství v úkolu, ke kterému byla stvořena, tedy v jednoduché vícenásobné náhradě prostého textu.

Mezi sestrami regulárkami nebyl favorit předem tolik zřejmý (alespoň pro mne). Jak je ale vidno z výsledků, preg svou sestřičku ereg naprosto rozdrtila. Jakmile začne ereg jen trochu expandovat, zadýchá se až hrůza.

Závěr

K jednoduchým vícenásobným náhradám textu se vyplatí použít str_replace. Ke všemu ostatnímu pak výborně poslouží preg_replace. Chudince ereg_replace můžeme tak leda vykopat hrob.

Hodnocení

Komentáře

2009-01-20 22:26:48

Zajímavé, napadlo mě, že by byla preg rychlejší, ale až tak? Čím to?

Na tento komentář odpověděl [3] Dundee
2009-01-20 23:40:25

Super srovnání! že je preg rychlejší jsem měl v podvědomí, ale že bude ereg_replace s náhradou expanzí tak pomalý jsem nečekal. Taky by mě zajímalo čím to je? Že by lepší (nativní) podpora Perl-compatible regulárů ze strany systému?

BTW. Nechceš se trochu rozepsat o Apache Bench? Moc ten nástroj neznám a docela by mě zajímal.

Na tento komentář odpověděl [3] Dundee
Na tento komentář odpověděl [4] Dundee
[3] Dundee
2009-01-21 00:33:07

#1 Shabbi.: #2 Tomáš Kraina: Také jsem tak drtivou porážku nečekal.

Jak je implementován ereg_replace se dá mrknout tady:

http://lxr.php.net/source/php-src/ext/ereg/ereg.c#370

Preg_replace je tady:
http://lxr.php.net/source/php-src/ext/pcre/php_pcre.c#1430

Moc moudrý z toho ale nejsem, neb Céčko není úplně moje silná stránka. Víc by mohl vědět David Grudl nebo Jakub Vrána. Ti dva si ty zdrojáky čtou před spaním ;)

[4] Dundee
2009-01-21 00:38:16

#2 Tomáš Kraina: Co se týče Apache Benche. Ono ani moc není o čem se rozepisovat. Je to konzolový nástroj na testování výkonu Apache serveru. Prostě napíšeš (Linux):

ab -n počet_dotazů -c počet_vláken URL

A ono ti to vyplivne všemožné statistiky. Nejzajímavější je pak právě počet dotazů za sekundu, které to zvládlo. Umí to samozřejmě i víc věcí jako použít proxy, odeslat POST atd.

2009-01-21 08:55:12

mrkněte na nový portál kliknetezde.cz

2009-01-21 09:49:07

Sice čisté céčko není můj šálek čaje, ale mám pocit, že rozdíl v rychlosti je ten, že preg_replace využívá tento algoritmus - http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm

a ereg_replace prostě načítá postupně řetězec a hledá co může nahradit. Ale jak říkám je to jen můj odhad.

2009-01-21 10:48:54

Zajímavé výsledky. že v prostém nahrazení vyhraje str_replace mi bylo jasné okamžitě. To že preg je o něco rychlejší než ereg již taky vím dávno, ale že je to až tak extrémní rozdíl? to by mě nenapadlo.
Jinak další zajímavé výsledky by určitě přinesl test funkci strpos a eregi. :-)

if (false !== strpos($text, "text")) || if (eregi("text", $text))
Nikdy jsem to neměřil, ale předpokládám že rozdíl bude naprosto katastrofální.

2009-01-21 10:54:29

díky. zajímavé srovnání

rozšíření ereg bude v PHP 5.3 deprecated a v PHP 6.0 bude odstraněno. pokud jej ještě někdo používáte, nahraďte jej sestřičkou preg

[9] Kačer
2009-01-21 11:41:21

A co srovnat ještě i strtr?

[10] vv
2009-01-21 12:05:12

Krome "mnohonasobne nahrady" je to srovnavani hrusek s jabky.
Na ostatni je lepsi pouzit kombinaci strpos() a substr(). Napr:
if (strpos($text, 'http://') === 0) substr($text, 7); //suverenne nejrychlejsi
Jinak to moc neodpovida tem RE.

Na tento komentář odpověděl [11] Dundee
2009-01-21 12:21:29

#10 vv: A představte si, že rychlejší není :)

Kombinace strpos + substr: 23 dotazů za sekundu, 81ms

Preg_replace: 29 dotazů za sekundu, 64ms

[12] vv
2009-01-21 12:37:45

U mne ano.
Cas behu toho kodu 10000x ve smycce:
0.0248870849609 s. replace
0.0150830745697 s. substr
0.0222339630127 s. preg
0.0628299713135 s. ereg
PHP5.2.4 / Apache2 / Ubuntu

2009-01-21 13:55:31

Zajímavé. Pokud vypnu Xdebug, dostanu se ke stejným výsledkům. Zdá se, že Xdebug zvyšuje režii volání funkcí.

Po vypnutí:

strpos + substr: 15ms
preg_replace: 23ms
ereg_replace: 58ms

PHP 5.2.6 / Apache 2.2.9 / Ubuntu 8.10

2009-01-21 20:18:22

Ten první kód:

if (strpos($text, 'http://') === 0) str_replace('http://', '', $text);

dělá něco malinko jiného, než ostatní, tj. nahrazuje výskyty v celém řetětci. Místo str_replace by mělo být substr(). Např:

if (strncmp($text, 'http://', 7) === 0) $text = substr($text, 7);

Jinak ereg*** funkce jsou obecně velmi pomalé a uvažuje se o jejich vyřazení z PHP.

Komentáře již nelze přidávat