【问题标题】:slow byte code with tail recursion尾递归的慢字节码
【发布时间】:2015-10-01 14:54:52
【问题描述】:

受到this SO question 答案的启发,我用代码检查了一个命令式循环以防止尾递归:

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

对于字节码和本机代码,结果是

str@s131-intel:~> ./bench_loop
recursive function: 20.7656 s
while loop with ref counter buitin incr: 12.0642 s
str@s131-intel:~> ./bench_loop.opt 
recursive function: 0.755594 s
while loop with ref counter buitin incr: 0.753947 s

问题是:20到12秒的执行时间相差这么大的原因是什么?

编辑,我的结论:

函数调用apply(字节码)涉及堆栈大小检查、可能的堆栈扩大和信号检查。为了获得最佳性能,本机代码编译器将提供。

(旁注:在此处询问 SO,因为它对搜索引擎友好。)

【问题讨论】:

  • opt in ocamlopt 代表优化。字节码编译器执行较少的优化,因为它从来都不是它的目的。尽管仍然进行了许多优化。例如,在当前版本的编译器 (4.03) 上,差异约为 10%(9.3 对 8.3 秒)。

标签: recursion ocaml performance-testing tail-recursion


【解决方案1】:

查看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)))

显然assignapply 快。似乎在函数调用时检查堆栈溢出和信号,但不是简单的分配。详情请看:https://github.com/ocaml/ocaml/blob/trunk/byterun/interp.c

【讨论】:

  • 好的,找到了。这看起来像一个 Forth 解释器。
猜你喜欢
  • 1970-01-01
  • 2019-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-21
  • 2018-03-17
相关资源
最近更新 更多