2015-07-13 27 views
5

Zainspirowany odpowiedzi this SO question wziąłem kod, aby sprawdzić się imperatyw pętlę przed ogonowej rekursji:powolny kod bajtowy z ogona rekurencji

let rec nothingfunc i = 
    match i with 
    | 1000000000 -> 1 
    | _ -> nothingfunc (i+1) 

let nothingloop1() = 
    let i = ref 0 in 
    while !i < 1000000000 do incr i done; 
    1 

let timeit f v = 
    let t1 = Unix.gettimeofday() in 
    let _ = f v in 
    let t2 = Unix.gettimeofday() in 
    t2 -. t1 

let() = 
    Printf.printf "recursive function: %g s\n%!" (timeit nothingfunc 0); 
    Printf.printf "while loop with ref counter buitin incr: %g s\n%!" (timeit nothingloop1()); 

Dla kodu bajtowego i kodu natywnego wyniki są

[email protected]:~> ./bench_loop 
recursive function: 20.7656 s 
while loop with ref counter buitin incr: 12.0642 s 
[email protected]:~> ./bench_loop.opt 
recursive function: 0.755594 s 
while loop with ref counter buitin incr: 0.753947 s 

Pytanie brzmi: jaki jest powód dużej różnicy 20 do 12 sekund czasu realizacji?

Edit, moja konkluzja:

Wywołanie funkcji apply (w kodu bajtowego) obejmuje sprawdzenie stosu rozmiar, ewentualnego rozszerzenia stosu i czek na sygnałach. Aby uzyskać maksymalną wydajność, dostarczony zostanie kompilator natywnego kodu.

(uwaga Side. Prosząc tutaj na SO, ponieważ jest przyjazny dla wyszukiwarek)

+1

opt w ocamlopt oznacza zoptymalizowany. Kompilator Bytecode wykonuje mniej optymalizacji, ponieważ nigdy nie był jego celem. Chociaż wiele optymalizacji nadal jest wykonywanych. I na przykład na aktualnej wersji kompilatora (4.03) różnica wynosi około 10% (9,3 vs 8,3 sekundy). – ivg

Odpowiedz

4

spojrzenie na wyjściu ocamlfind ocamlc -package unix test.ml -dlambda

(nothingloop1/1010 = 
    (function param/1022 
     (let (i/1011 =v 0) 
     (seq (while (< i/1011 100000000) (assign i/1011 (1+ i/1011))) 1))) 

(nothingfunc/1008 
    (function i/1009 
    (if (!= i/1009 100000000) (apply nothingfunc/1008 (+ i/1009 1)) 1))) 

Więc widocznie assign jest szybszy niż apply. Wydaje się, że sprawdzane są przepełnienia stosu i sygnały podczas wywoływania funkcji, ale nie dla prostego przypisania. Aby uzyskać szczegółowe informacje, należy spojrzeć na: https://github.com/ocaml/ocaml/blob/trunk/byterun/interp.c

+0

Dobra, znalazłem. To wygląda na tłumacza Fortha. –

Powiązane problemy