Megnéztem még a dev ágat (2.5.0) a mai nightly snapshottal, ugyanúgy forrásból fordítva, eredmény: milliszekundumra egyezik az átlag a 2.4-es verzióval.
Csináltam még egy apró szintetikus tesztet fibonacchi sorozattal, aminek nincs sok értelme, de érdekes lassúságot mutat Rubinius itt is - úgy látszik az elmúlt pár évben jól ráhúzott MRI (Rubinius 2.77x lassabb):
def f(n); return n if n <= 1; return f(n-1) + f(n-2); end
require "benchmark"
puts Benchmark.realtime { f 40 }
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]
=> 14.174080050001066
rubinius 3.84 (2.3.1 3970f17d 2017-08-02 3.8.0) [x86_64-linux-gnu]
=> 39.267210773
További pár értelmetlen teszt:
Python:
def f(n):
if n <= 1:
return n
return f(n-1) + f(n-2)
import time
t = time.time()
f(40)
print (time.time() - t)
2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
=> 35.6807920933
3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609]
=> 41.74373817443848
2.7.10 (5.1.2+dfsg-1~16.04, Jun 16 2016, 17:37:42) [PyPy 5.1.2 with GCC 5.3.1 20160413]
=> 4.68786501884
PHP:
<?php
function f($n) {
if ($n <= 1) {
return 1;
}
else {
return f($n-1) + f($n-2);
}
}
$t = microtime(true);
f(40);
echo (microtime(true) - $t);
?>
PHP 7.0.22-0ubuntu0.16.04.1 (cli) ( NTS )
=> 11.148350954056
Perl:
sub f { return @_[0] < 2 ? @_[0] : f(@_[0]-1) + f(@_[0]-2); }
use Time::HiRes qw( time );
my $t = Time::HiRes::gettimeofday();
f(40);
print (Time::HiRes::gettimeofday() - $t);
This is perl 5, version 22, subversion 1 (v5.22.1) built for x86_64-linux-gnu-thread-multi
=> 107.15203499794
Összesítés, Fibonacchi(40):
4.7s pypy 5.1.2
11.1s php 7.0.22
14.2s ruby 2.4.1
35.7s python 2.7.12
39.3s rubinius 3.84
41.7s python 3.5.2
107.1s perl 5.22.1
- log69 blogja
- A hozzászóláshoz be kell jelentkezni
- 1159 megtekintés
Hozzászólások
Aki nem tud arabusul, az ne beszéljen arabusul.
perl -MTime::HiRes=time -e 'sub f { @_[0] < 2 ? @_[0] : f(@_[0] - 1) + f(@_[0] - 2) } my $t = time; f(40); print time - $t'
66.8140659332275
perl -MTime::HiRes=time -e 'sub f { $_[0] < 2 ? $_[0] : f($_[0] - 1) + f($_[0] - 2) } my $t = time; f(40); print time - $t'
44.8093011379242
This is perl 5, version 24, subversion 2 (v5.24.2) built for x86_64-linux-thread-multi
(A ruby-változat 10.0 s ugyanitt)
(Az mondjuk szomorú, hogy még így is a perl a leglassabb - régen arról volt híres, hogy a szkriptnyelvek között ő a leggyorsabb. Igaz, azt is tudjuk, hogy a függvényhívás aránylag drága művelet Perlben, ez a teszt meg pont arra van kihegyezve.)
(Másfelől
perl -MTime::HiRes=time -MMemoize -e 'memoize("f"); sub f {$_[0]<2?$_[0]:f($_[0]-1)+f($_[0]-2)} my $t = time; f(40); print time - $t'
0.000284910202026367
)
- A hozzászóláshoz be kell jelentkezni
Az első perl résznél nem látom mit változtattál érdemben.
- A hozzászóláshoz be kell jelentkezni
$_[0] vs @_[0]
Az előbbi visszaadja a @_ tömb (amelyben az argumentumok adódnak át a függvénynek) 0. elemét skalárként. Az utóbbi pedig egy ún. array slice, azaz egy egy elemű tömb, az eredeti @_ tömb 0. elemével.
Véletlenül működik és ugyanazt az eredményt adja ebben az esetben, de nem ugyanaz a kettő.
- A hozzászóláshoz be kell jelentkezni
Köszi.
- A hozzászóláshoz be kell jelentkezni
Ez a perl tényleg annyira undorító, mintha úgy született volna, hogy egy csapat autista programozót begombázva bezárnak egy hétre a pincébe, hogy programnyelv nélkül nem jöhetnek ki a szabad levegőre.
--
arch,debian,retropie,osmc,android,windows
- A hozzászóláshoz be kell jelentkezni
Mellékelem a fenti C-re átírt változatát (fibo.c):
#include <time.h>
#include <stdio.h>
int f(int n) {
if (n <= 1) return n;
return f(n-1) + f(n-2);
}
int main() {
struct timespec gstart, gend;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &gstart);
int fib = f(40);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &gend);
double eltime = (gend.tv_sec - gstart.tv_sec) + (gend.tv_nsec - gstart.tv_nsec)/1000./1000./1000.;
printf("eredmeny: %d, ido: %f mp\n", fib, eltime);
return 0;
}
Fordítása: gcc -O2 fibo.c -o fibo
Futásidő normalizálva log69 számítógépének tempójára:
0,7s C (gcc-7)
4.7s pypy 5.1.2
11.1s php 7.0.22
14.2s ruby 2.4.1
35.7s python 2.7.12
39.3s rubinius 3.84
41.7s python 3.5.2
107.1s perl 5.22.1
- A hozzászóláshoz be kell jelentkezni
@hg2ecz: kösz, ez érdekes.
- A hozzászóláshoz be kell jelentkezni
És még egy érdekes: lua szkript de luajit-tel futtatva.
#!/usr/bin/luajit
function f(n)
if n <= 1 then
return n
end
return f(n-1) + f(n-2)
end
local t = os.clock()
f(40)
print ( os.clock() - t )
A futásidő log69 számítógép tempójára normalizálva:
0.7s C (gcc-7)
1.7s luajit 2.0.4
4.7s pypy 5.1.2
11.1s php 7.0.22
14.2s ruby 2.4.1
30,5s lua5.3
35.7s python 2.7.12
39.3s rubinius 3.84
41.7s python 3.5.2
107.1s perl 5.22.1
- A hozzászóláshoz be kell jelentkezni
@hg2ecz: LUAJit érdekes. Én most futtattam még JRuby-t (1.7.22 (1.9.3p551)), ugye ez Java JVM-en fut bytecode-ra fordulás után, 5.5 sec alatt fut.
Közben buildeltem friss JRuby-t (9.1.12.0 (2.3.3)), ezzel 7.4 sec
- A hozzászóláshoz be kell jelentkezni
És egy népszerű szkriptnyelv: javascript & nodejs
#!/usr/bin/nodejs
function f(n) {
if (n <= 1) return n
return f(n-1) + f(n-2)
}
var t = new Date()
f(40)
console.log((new Date() - t)/1000.)
És a normalizált tempó:
0.7s C (gcc-7)
1.7s luajit 2.0.4
2.2s nodejs v4.8.4
4.7s pypy 5.1.2
11.1s php 7.0.22
14.2s ruby 2.4.1
30,5s lua5.3
35.7s python 2.7.12
39.3s rubinius 3.84
41.7s python 3.5.2
107.1s perl 5.22.1
- A hozzászóláshoz be kell jelentkezni
-
- A hozzászóláshoz be kell jelentkezni
Tovább a fun kedvéért, sima JS-t futtatva FF 55-ön 1.4 sec illetve Chromium-on 1.7 sec.
https://frontfoo.com/main?user_main=f&pass_main=f&menu=code&uuid=368963…
- A hozzászóláshoz be kell jelentkezni
0.7s C (gcc-7)
1.1s js (epiphany 3.18.11)
1.4s js (firefox 55.0.2)
1.7s js (chrome 60.0.3112.78)
1.7s luajit 2.0.4
1.9s js (midori 0.5.11)
2.2s nodejs v4.8.4
4.7s pypy 5.1.2
5.5s jruby 1.7.22
7.4s jruby 9.1.12.0
11.1s php 7.0.22
14.2s ruby 2.4.1
30,5s lua5.3
35.7s python 2.7.12
39.3s rubinius 3.84
41.7s python 3.5.2
107.1s perl 5.22.1
- A hozzászóláshoz be kell jelentkezni
Még egy érdekes témáról ne feledkezzünk meg: eddig a programozási nyelvek futásteljesítményét vizsgáltuk, kihegyezve a rekurzióra.
Nézzük meg a programozó szerepét. Fibonacci, de nem rekurziv módon és legyen most Javascript.
#!/usr/bin/nodejs
function f(n) {
var p1 = 1,
p2 = 0,
fibo = 0;
for (var i = 1; i < n; i++) {
fibo = p2 + p1;
p2 = p1;
p1 = fibo;
}
return fibo;
}
var t = new Date()
console.log( f(40) )
console.log((new Date() - t)/1000.)
Ugyanazt a végeredményt számolva, de máshogy megírt algoritmussal, jelen esetben a rekurzió kerülésével kb. 150-szeresére gyorsítottuk.
- A hozzászóláshoz be kell jelentkezni
Igen, de nem 150x, hanem akár sok milliószoros, mert itt most 2 monoton növekvő görbénk van, csak a meredekségük különböző, és ahogy haladunk jobbra az x tengelyen egyre nagyobb fibo értéket kiszámolva, úgy nő exponenciálisan a két algo közti sebesség (idő) különbség.
Igazából a rekurzív algo nyilván csak egyetlen keresztmetszetet ad, de azért érdekes látni értékeket ebből a szempontból is. Ha lesz időm, lefuttatom a nem rekurzívat több nyelvre.
- A hozzászóláshoz be kell jelentkezni
Kérdés még ennél az algonál, hogy nem fut-e az egész szám értéke a határokon túl, mert Ruby-nál nem tud ugye és ott lefut normálisan. Viszont JS alatt böngészőn belül túl gyorsan fut le, de ugye ott gyorsan túl is csordul (Infinity). Tehát nehéz érdemben tesztelni így egymáshoz képest a nyeveket ezzel az algo-val. C-ben ugye nem is tudod ábrázolni a fibo(500_000) értéket, hiába a 64 bit integer (2^64 körülbelül fibo(92)-vel egyenlő).
Szerk.:
PHP és Lua sem tesztelhető ezért ahogy látom. Túl gyorsan lefut a túlcsordulás miatt.
Szerk.:
fibo(500_000) körülbelül 2^347_120 és 10^104_494 értékkel egyenlő.
- A hozzászóláshoz be kell jelentkezni
Igen, ezzel célszerű tisztában kell lenni. A Python integer osztálya figyel a túlcsordulásra és nagy számokat automatikusan szoftverből elvégzett aritmetikával kezel.
C estén 64 bit, illetve __int128 (64 bites OS-eken) típus áll rendelkezésedre például a 64 bites szorzások eredményének átvételéhez. Ellenben natív proci sebességen adja.
Utána jöhet a gmp vagy boost lib, amely szoftverből számol tovább.
Példakód C-ben (szedd ki a megfelelő // -t a típusválasztáshoz):
#include <stdio.h>
// ---> __fp16 IEEE754:2008 ... ARM-okon megy
//#define TYPE __fp16
//#define TYPE float
//#define TYPE double
//#define TYPE long double
//#define TYPE __float128
// ---> unsigned +1 bit
//#define TYPE byte
//#define TYPE short
//#define TYPE int
//#define TYPE long long
//#define TYPE __int128
int main() {
TYPE ro, r2, r = 1;
for (int i = 0; i < 9999; i++) {
ro = r;
r *= 2;
r2 = r+1;
if (r <= ro || r2 == r) {
printf("Felbontas: %d bit\n", i+1);
break;
}
}
return 0;
}
A szkriptnyelvek numerikus értéknek gyakran double-t használnak. Lásd alábbi teszteket:
#!/usr/bin/nodejs
var ro, r2, r = 1;
for (var i = 0; i < 9999; i++) {
ro = r;
r *= 2;
r2 = r+1;
if (r <= ro || r2 == r) {
console.log("Felbontas: %d bit\n", i+1);
break;
}
}
#!/usr/bin/luajit
local ro = 0
local r2 = 0
local r = 1
for i=0, 9999 do
ro = r
r = r*2
r2 = r+1
if r <= ro or r2 == r then
print("Felbontas: ", i+1, "bit");
break;
end
end
#!/usr/bin/php
<?php
$r = 1;
for ($i = 0; $i < 9999; $i++) {
$ro = $r;
$r *= 2;
$r2 = $r+1;
if ($r <= $ro || $r2 == $r) {
printf("Felbontas: %d bit\n", $i+1);
break;
}
}
?>
C: típusfüggő, unsigned __int128 (128 bit) és __float128 (112 bit felbontás) a natív CPU aritmetika felső határa.
NodeJS, LuaJIT: 53 bit (double)
PHP7: 63 bit, de $r2 = $r+1.5; esetén 54 bit.
Python, Ruby: nincs a számábrázolásnak felső határa.
- A hozzászóláshoz be kell jelentkezni
C-ben nem tudnál összeütni egy olyan algo-t, amely "arbitrary long integer"-t tud használni?
- A hozzászóláshoz be kell jelentkezni
GMP a barátunk. A C-beli (processzorbeli) aritmetika helyett szoftveresen összerakott aritmetikával dolgozik.
Ellenben ezzel a teszttel eljutunk oda, hogy nem az adott nyelven megírt algoritmus futását teszteljük, hanem azt nézzük, mely programnyelv miként használja fel ugyanazt a C-ben megírt libgmp-t.
#include <gmp.h>
#include <time.h>
#include <stdio.h>
mpz_t *f(int n) {
static mpz_t fibo;
mpz_t p1, p2;
mpz_init_set_ui(p1, 1);
mpz_init(p2);
for (int i = 1; i < n; i++) {
mpz_add(fibo, p2, p1);
mpz_add_ui(p2, p1, 0);
mpz_add_ui(p1, fibo, 0);
}
mpz_clear(p1);
mpz_clear(p2);
return &fibo;
}
int main() {
struct timespec gstart, gend;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &gstart);
mpz_t *res = f(40);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &gend);
mpz_out_str(stdout, 10, res); puts("");
double eltime = (gend.tv_sec - gstart.tv_sec) + (gend.tv_nsec - gstart.tv_nsec)/1000./1000./1000.;
printf("ido: %f mp\n", eltime);
return 0;
}
- A hozzászóláshoz be kell jelentkezni
"ezzel a teszttel eljutunk oda.."
Egyetértek. 1.9s lett amúgy.
- A hozzászóláshoz be kell jelentkezni
De ez is kis trükkel felezhető. Hagyjuk el a GMP-beli másolást.
#include <gmp.h>
#include <time.h>
#include <stdio.h>
mpz_t *f(int n) {
static mpz_t prev[4];
mpz_init_set_ui(prev[0], 1);
mpz_init(prev[1]);
mpz_init(prev[2]);
mpz_init(prev[3]);
int i;
for (i = 1; i < n; i++) {
mpz_add(prev[i&3], prev[(i-2)&3], prev[(i-1)&3]);
}
return &prev[(i-1)&3];
}
int main() {
struct timespec gstart, gend;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &gstart);
mpz_t *res = f(500000);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &gend);
mpz_out_str(stdout, 10, res); puts("");
double eltime = (gend.tv_sec - gstart.tv_sec) + (gend.tv_nsec - gstart.tv_nsec)/1000./1000./1000.;
printf("ido: %f mp\n", eltime);
return 0;
}
- A hozzászóláshoz be kell jelentkezni
0.9 sec
- A hozzászóláshoz be kell jelentkezni
Ruby:
def f2(n)
p1 = 1
p2 = 0
fibo = 0
while (n > 1) do
fibo = p2 + p1
p2 = p1
p1 = fibo
n -= 1
end
return fibo
end
require "benchmark"
puts Benchmark.realtime { x = f2(500_000) }
Python:
def f2(n):
p1 = 1
p2 = 0
fibo = 0
while (n > 1):
fibo = p2 + p1
p2 = p1
p1 = fibo
n -= 1
return fibo
import time
t = time.time()
x = f2(500000)
print (time.time() - t)
fibo 500_000:
2.0s pypy 2.7.10
2.7s python 2.7.12
2.7s python 3.5.2
3.0s ruby 2.4.1
5.2s jruby 1.7.22 (1.9.3p551)
5.2s jruby 9.1.12.0 (2.3.3)
10.2s rubinius 3.84 (2.3.1)
- A hozzászóláshoz be kell jelentkezni