Dar vieno analitiko svetainė

Petras Kudaras

Įpraiškų greičio testai

Šiandien visą dieną rašiau kursinį darbą (tikrai ne apie programavimą… Apie valstybės išlaidų skirstymą pagal valstybės funkcijas bei struktūrinių pokyčių tendencijas :). Tai vakare sugalvojau kad reikia truputį pailsėt ir nuėjau pasibandyt įpraiškų greičių.

Tarkim turim stringą „Čia gali būti tiek Perlas, tiek Pitonas“ ir norim patikrint ar jame yra „Perl“ arba „Python“ (šiuo atveju yra tik „Perl“, nes „Python“ parašytas su trumpa „i“). Šiam tikslui tiktų labai paprasta įpraiška (tiems, kas jau nori rėkti „bet čia juk nereikia įpraiškos!“ pasakysiu „perskaitykit iki galo“ ;)

/Perl|Python/

Aišku šitą dalyką galima optimizuoti, nes abu žodžiai prasideda iš „P“:

/P(erl|ython)/

Viskas gerai, tik tuos skliaustus visada permeta į $1, kas stabdo visą reikalą. Prisiminus išplėstą įpraiškų sintaksę:

/P(?:erl|ython)/

Turbūt nieko geriau jau neišeis išspaust iš šios įpraiškos. Na bet jei jau prisiminėm išplėstą įpraiškų sintaksę, tai dar vieną daiktą galima pridėt (čia jau pavyzdys stiliumi „Nuo didelio rašto išėjo iš krašto“):

/(?<=P)(?:erl|ython)/

Na, palyginimui dar reiktų pridėti ir sprendimą be įpraiškos:

(((index $f, 'Perl') != -1) ||
 ((index $f, 'Python') != -1)) ? 1 : 0;

Na, turim visus variantus, tai reikia juos išsitestuoti:

#!/usr/bin/perl
$f ="Čia gali būti tiek Perlas, tiek Pitonas";

sub regnormal { $f =~ /Perl|Python/;         }
sub regnonopt { $f =~ /P(erl|ython)/;        }
sub regopt    { $f =~ /P(?:erl|ython)/;      }
sub regfancy  { $f =~ /(?<=P)(?:erl|ython)/; }

sub strstr {
(((index $f, 'Perl') != -1) ||
 ((index $f, 'Python') != -1)) ? 1 : 0;
}

use Benchmark;
timethese(-10, { regnormal => 'regnormal', 
                 regnonopt => 'regnonopt',
                 regopt    => 'regopt',
                 regfancy  => 'regfancy',
                 strstr    => 'strstr' });

Štai rezultatai:

C:devellab>perl regex.pl
Benchmark: running regfancy, regnonopt, regnormal, regopt, strstr, for at least 10
CPU seconds...
  regfancy: 17 wallclock secs (12.28 usr +  0.04 sys = 12.32 CPU) @ 67725.60/s
(n=834244)
 regnonopt: 14 wallclock secs (10.00 usr +  0.04 sys = 10.04 CPU) @ 82688.20/s
(n=830603)
 regnormal: 15 wallclock secs (10.10 usr +  0.02 sys = 10.12 CPU) @ 52365.33/s
(n=530199)
    regopt: 14 wallclock secs (10.13 usr +  0.01 sys = 10.14 CPU) @ 154168.16/s
(n=1564036)
    strstr: 13 wallclock secs (10.15 usr + -0.01 sys = 10.14 CPU) @ 272950.11/s
(n=2768533)

Aišku nenuostabu, kad variantas be įpraiškų buvo pats greičiausias. Gal net esu šiek tiek nustebęs, kad jis greitesnis už įpraišką tik du kartus (tikėjausi bent kokių penkių kartų). Viso šio dalyko pamoka tokia: nenaudokite įpraiškų, jei galima išsiversti be jų (na, arba jei be jų kodas susidėtų iš daugybės sudėtingų if – jau ir tas subas, kurį parašiau vizualiai atrodo baisiau už įpraiškas). Jei reikia įpraiškos – skliaustelius rašykite tik gerai viską apgalvoję arba naudokitės išplėstine sintakse.

Nežinau kaip viskas veiktų kitose kalbose. Esmė turbūt liktų ta pati (Ar PHP palaiko (?:…) ?)