Dar vieno analitiko svetainė

Petras Kudaras

Perl vs PHP vs Ruby

Taip jau visad gaunasi kad kai reikia mokytis tai darai bet ką, tik ne mokaisi. Tad šiandien padariau dar vieną benchmarką, kuris gal būt bus įdomus ir kitiems.

Problema tokia: reikia programos, kuri nuskaitytų bylą ir suskaičiuotų kiek yra skirtingų simbolių (tarkim „a“ – 152 kartai, „b“ – 189, ir panašiai). Algoritmas paprastas ir visur vienodas: nusiskaitom bylą po eilutę, skaidom į simbolių masyvą, pereinam per šį masyvą, padidindami atitinkamas reikšmes saugomas asociatyviajame masyve (jei skamba sudėtingai, tai žiūrėkit kodą, ten aiškiau ;)

Pirmiausia parašiau Perlinį variantą. Užtruko apie 2-3 minutes (rašymo trukmė maža, nes Perlas man gerai žinomas, nereikia per daug nieko galvoti, esu prie jo pripratęs):

use Time::HiRes qw/gettimeofday tv_interval/;
my $t0 = [gettimeofday];
while(my $h = <>) { $_{$_}++ for split //, $h; }
print "$_ => $_{$_}\n" for keys %_;
print "Laikas: ", tv_interval($t0, [gettimeofday]);

Eilučių skaičius – apie 5 (sakau „apie“, nes ciklus galiam skirti ir kaip atskiras eilutes. Čia priklauso nuo to ką laikote eilute. Ilgiausiai laiko rašant užtruko modulio Time::HiRes dokumentacijos pasižiūrėjimas, nes jo dažnai nenaudoju (be to, jis čia atlieka pagalbinę laiko skaičiavimo funkciją)

Toliau parašiau PHP programėlę (kuri eitų per CLI). Užtruko apie 10 minučių, dėl poros priežasčių. Pirma, reikia spausdinti šiek tiek daugiau kodo, antra, buvau nustebęs kad explode negali veikti su tuščiu stringu (explode(’’, $foo) meta klaidą). Nuėjau pasiskaityti dokumentacijos, bet lyg neradau kitos alternatyvos preg_split. Štai PHP variantas:

<?php
function getmicrotime() { 
    list($usec, $sec) = explode(' ',microtime()); 
    return ((float)$usec + (float)$sec%10000); 
    } 
$time_start = getmicrotime();

$f = fopen($argv[1], 'rb');
while (!feof ($f)) {
    $m = preg_split('//', fgets($f, 4096));
    foreach ($m as $v) {
        $res[$v]++;
    }
}
fclose($f);
foreach($res as $k => $v) {
        print "$k => $v\n";
}
print "Laikas: ".(getmicrotime()-$time_start);
?>

Eilučių: apie 11. getmicrotime() funkcija paimta tiesiai iš manualo.

Įkvėptas greitos sėkmės sugalvojau kad reikia pabandyti parašyti ir Ruby programą. Vat čia užtrukau. Kokias 40 minučių. Ruby aš žinau šiek tiek tik teoriją, ir tai seniai ją skaičiau, todėl teko paskaityti dokumentaciją. Be to, mano Ruby programa, nors ir veikia, atrodo labai keistai, labai jau perliškai. Įtariu jog Ruby prastas pasirodymas benchmarkuose yra tiesiog mano kreivų rankų produktas. Štai rubinas:

t = "%10.5f" % Time.now.to_f
f = File.new($*[0])
h= {}
f.each { |line| 
    line.split(//).each { |char|
        h[char] = h[char].to_i+1;
    }
}
h.each { |k, v| print "#{k} => #{v}\n" }
print "Laikas: ", (("%10.5f" % Time.now.to_f).to_f - t.to_f).to_s

Eilučių: apie 8. Labiausiai keistas dalykas, kuris turi daug reikšmės benchmarke, ir kuris tikrai kažkaip kitaip turėtų būt išsprendžiamas yra tas, kad nil kažkodėl negalima automatiškai pasiversti į skaičių „0“. Įtariu kad reikia apsirašyt savą metodą vienos eilutės tam. Be to labai bjauriai su tais laiko matavimais ir tipų keitimais gaunasi :/ Matyt aš ten dar visai kampo nepagavęs esu ;)

Pats benchmarkas buvo atliekamas su 1,5MB tekstine byla (#php kanalo logas ;). Rezultatai (sekundėmis): Perl 13.6896, PHP 27.4513, Ruby 61.4180

Ką tai reiškia? Visiškai nieko. Perlas greitesnis, nes čia ir yra jo pritaikymo sritis. Perlas čia dar greitesnis ir dėl to, kad aš jį geriau moku nei kurią kitą kalbą. Tad jei esate PHP diveas, o Perlo visai nemokat tai jokiu būdu neapsimoka jums mesti PHP – tai labai gerai iliustruota mano bandymais rašyti Ruby kalba (kituose normalių žmonių benchmarkuose Ruby greičiu prilygsta Perlui). Ruby programa ne tik lėčiau veikė, bet dar ir aš ją rašiau žymiai ilgiau.

Faktiškai visa šitai tik parodo kad MAN Perlas yra geriausias pasirinkimas, galbūt jau vien dėl to, kad aš jį moku geriausiai. (Juk sakiau kad stengsiuos atsikratinėt fanatizmu ;)