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 ;)

Comments Closed

9 Comments

  1. o tu dar drysti sakyti, kad tu neesi adminas, taigi Perl = Practical extraction and reporting language :p

  2. Pamėginau pas save. Perl skriptas sukasi 8,5 sekundės. PHP: 17 su trupučiu sekundės.

    Modifikacija tipo:

    $f = file($argv[1]);

    $n=count($f);

    for ($i=0;$i<$n;$i++) {

    laimi sekunde.

    Bet tai niekai.

    Variantas:

    <?php

    function getmicrotime() {

    list($usec, $sec) = explode(' ',microtime());

    return ((float)$usec + (float)$sec%10000);

    }

    $time_start = getmicrotime();

    $f = join('',file($argv[1]));

    $res=count_chars($f,1);

    foreach($res as $k => $v) {

    print chr($k)." => $v\n";

    }

    print "Laikas: ".(getmicrotime()-$time_start);

    ?>

    sukasi 7,74 sekundes.

    Bet Perlas man vis tiek patinka :)

  3. Nu viso failo nuskaitymas vienu kartu į atmintį tai ir Perlą pagreitina. Tipo:

    undef $/; $_{$_}++ for split //, <>; …

  4. viens nedurnas mokslininkas (ne moxlininkas:)

    tyrinejo tyrinejo kalbu greicius

    po to galu gale pasidave ir pareishke:

    Performance difference due to programmer variability is greater than that due to language variability.

    todel, kad kuo didesne uzduotis, tuo daugiau budu ja atlikti, ir nebutinai tam reikia perlo :) patys matom, kad su php irgi visaip ishsivartyti gali.

  5. Kitaip sakant atmetus visus laiku spausdinimus

    PHP:

    $f = join('',file($filename));

    $res = count_chars($f,1);

    foreach ($res as $k => $v)

    echo chr($k)." => $v\n";

    o su PERL:

    while(my $h = <>){$_{$_}++ for split //, $h; }

    print "$_ => $_{$_}\n" for keys %_;

    Kokias ish to galime padaryti ishvadas? :P Ogi tokias, kad adminams tinka ir patinka PERL, ypac jei jie ji jau moka. Taip pat dristu daryti ishvada, kad gerb. moxliuko benchmarkais galima pasitiketi ne daugiau nei Majkrosofto.

  6. BTW, PHP pavyzdi irgi galima sutraukti iki 2 eiluciu:

    $res = count_chars(join('',file($filename)),1);

    foreach ($res as $k => $v) {echo chr($k)." => $v\n";}

  7. Šiaip tai jei nepastebėjot, tai aš būtent ir padriau išvadą, kad reikia programuoti tuo kuo JAU moki ir tada greičiausiai gaunasi ;)

    Tad man geriausia yra perlas, jums — PHP. Dar kam nors — Visual Basic ;)

  8. shell'e awk'u kazkaip aiskiau ir paprasciau :)

    blah:/tmp$ time awk -F '' '{for(i=1;i<=NF;i++){rds[$i]

    ++}}''END{for(r in rds)printf("%s-%s\n",r,rds[r])}' failas

    0m8.531s (sis baisus variantas awk'u) pries 0m12.683s

    (perl'u)

Comments are closed.