Ocaml 为什么Array.map比tail递归map(相当)快?

Ocaml 为什么Array.map比tail递归map(相当)快?,ocaml,Ocaml,我编写了map\u tail的尾部递归版本,如下所示: let map_tail f l = let rec map acc = function | [] -> List.rev acc | hd::tl -> map (f hd :: acc) tl in map [] l 然后是一个基于数组的逐数组映射: let map_by_array f l = Array.of_list l |> Array.map f |> Array.

我编写了
map\u tail
的尾部递归版本,如下所示:

let map_tail f l =
  let rec map acc = function
    | [] -> List.rev acc
    | hd::tl -> map (f hd :: acc) tl
  in 
  map [] l
然后是一个基于数组的
逐数组映射

let map_by_array f l =
  Array.of_list l |> Array.map f |> Array.to_list

下面是基准代码

let ran_list n =
  Random.self_init();
  let rec generate acc i =
    if i = n then acc
    else generate (Random.int 5::acc) (i+1)
  in 
  generate [] 0

let _ =
  let l = ran_list 10000000 in
  let f x = x+1 in
  let t1 = Sys.time() in
  let l1 = map_tail f l in
  let t2 = Sys.time() in
  let l2 = map_by_array f l in
  let t3 = Sys.time() in
  Printf.printf "map_tail: %f sec\nmap_by_array: %f sec\n" (t3-.t2) (t2-.t1)

我发现基于阵列的贴图速度更快,这让我有点吃惊


map\u tail
中,它遍历列表
两次
,而
map\u by\u array
遍历列表
三次
,为什么速度更快?

这可能取决于列表的大小

在大小为N的长列表上,
map\u tail
将执行2*N分配(映射期间为N,然后为
List.rev
)分配N),而
map\u by\u array
将执行N+2分配(1个用于
Array.of_list
,1个用于
Array.map
,N个用于
Array.to_list
,实际上,这也可以优化为只进行一次分配)


因为分配可能是这个代码中最昂贵的操作,这个差异应该解释性能的差异。

你确定吗?我尝试了不同的场景,对我来说,有时第一个,有时第二个更快。考虑使用<代码> fx=一些(x+1)
。我认为纯数组遍历可能比列表遍历更快(因为数组可能占用连续内存,而列表有指针)。这里讨论了一些细节:@rafix是的,那篇文章让我这么做的experiment@phimuemue我做了
fx=Some(x+1)的实验
,基于数组的速度更快。
map\u tail:3.245189秒,map\u by\u array:2.242302秒
我也注意到它用于素数生成函数。在处理列表时,计算1 000以下的素数可能需要几个小时,而使用数组只需要12秒。我猜这是由于内存分配和垃圾收集离子。