La velocità delle funzioni PHP di ricerca su stringhe dipende dall'”ago”

Stavo giocando con alcuni test di prestazioni su dei templates nel mio post precedente, e sono incappato in una scoperta interessante riguardante il modo in cui funziona strpos, e conseguentemente tutte le altre funzioni di tipo “cerca (e sostituisci)” come str_replace, strrpos, stripos, str_ireplace, substr_replace, persino preg_match and preg_replace (ho testato solo str_replace e preg_match, ma suppongo ci siano gli stessi risultati per tutte le altre).

  1. Userò in questo articolo la convenzione di php.net, e chiamerò “ago” la stringa che state cercando, e “pagliaio” la stringa all’interno della quale effettuate la ricerca
  2. Conoscere questi risultati è utile in pratica solo se potete scegliere quali aghi inserire nel pagliaio, per poterli poi cercare successivamente, cioè se state costruendo un template
  3. Ho già cercato per controllare se questa “trovata” era già stata documentata da qualcun altro, ma così non sembra, quindi se mi fossi perso qualcosa evitate di aggredirmi

Mettiamo che stiate creando lo schema per un template, dove inserirete dei tag che dovranno essere sostituiti dai contenuti generati dinamicamente dal vostro sito. Potete chiamarli come volete purché siano stringhe uniche all’interno del modello, quindi per esempio %tag% o {tag} o <!–tag–> o ~tag~ (o come vi pare). Magari pensate che scegliere quale tipo di carattere di delimitazione usare per il nome del tag dipenda solo dai vostri gusti personali, e lo pensavo pure io prima di scoprire questa cosa per puro caso, comunque vi dico subito che se il template consiste in codice HTML ben formato, allora usare per esempio ~tag~ sarà molto più veloce che usare <!–tag–>.

Il principio generale è che cercare un ago che inizia con un carattere “raro” è molto più veloce che cercare un ago che inizia con un carattere molto comune all’interno del pagliaio.

Per esempio, se il vostro pagliaio è un estratto di un ebook, cercare “%ciao” (se presente) sarà molto più rapido che cercare “ciao”.  La giustificazione di questo? Evidentemente la funzione in C che cerca la stringa, inizia col cercare il primo carattere, e una volta trovato vede se quello successivo corrisponde, e così via; quindi cercando “ciao” la funzione incapperà in tutte le “c” per controllare se sono seguite da una “i”, se sì poi verificherà se c’è una “a”, ma se ad esempio la parola è “ciarpame”, la funzione verificando l’assenza della “o” finale dovrà scartare il lavoro ed il tempo impiegato, e proseguire. Invece il carattere “%” è raro, se non unico, all’interno di un testo “normale”, quindi la funzione dovrà “fermarsi” molte meno volte a controllare prima di trovare una corrispondenza piena.

Mettiamo questa teoria alla prova, ecco il codice:

$creationstart=strtok(microtime()," ")+strtok(" ");
for ($i=0;$i<100000;$i++) $testpos=strpos($test,"malesuarda");
$creationend=strtok(microtime()," ")+strtok(" ");
$creationtime=number_format($creationend-$creationstart,4);
echo "malesuarda $testpos: ".$creationtime."<br />";

$creationstart=strtok(microtime()," ")+strtok(" ");
for ($i=0;$i<100000;$i++) $testpos=strpos($test,"%malesuada");
$creationend=strtok(microtime()," ")+strtok(" ");
$creationtime=number_format($creationend-$creationstart,4);
echo "%malesuada $testpos: ".$creationtime."<br />";

Spieghiamo un po’ di cose: $test è una stringa relativamente lunga, definita precedentemente nel codice (è composta da un testo lorem ipsum con diversi paragrafi, per 13kb totali), all’interno della quale ho scelto una parola a caso, “malesuada“, che viene ripetuta diverse volte, e ho cambiato due occorrenze di questa parola, entrambe verso la fine della stringa, per renderle uniche, una è diventata malesuarda, cioè ho aggiunto una “r”, e l’altra (più avanti nella stringa) invece è stata modificata in %malesuada, quindi alla fine ho caricato lo script PHP; ho aggiunto un echo per $testpos in modo da confermare che le stringhe fossero state realmente trovate da strpos.

Come atteso, ecco i risultati:

malesuarda 10970: 3.5609
%malesuada 11514: 0.7632

Sostituendo strpos con qualunque altra funzione elencata all’inizio dell’articolo otterrete valori simili.

Leave a Reply

Your email address will not be published. Required fields are marked *