为什么在OCaml中应对/更新列表非常慢?

时间:2014-05-21 19:50:46

标签: arrays performance linked-list ocaml

所以基本上我在我的ocaml程序中使用list,原来它是这样的:

val mutable ll : string list = []
.....

ll <- ll@[(foo ar1 ar2)]

然后,当测试相对较大的数据集(超过50k)时,我的程序运行速度太慢。

我原以为是因为上面的代码中有整个列表副本进程,(每次ll <- ll@[]时)

所以我用这种方式改变了我的代码:

ll <- (foo ar1 ar2)::ll   (* extend the head for N times *)
.....
List.rev ll

然而,令我惊讶的是,似乎没有明显的性能提升。

然后我尝试了这样的数组:

let arr = Array.make len "" in 
  arr.(counter) <- (foo ar1 ar2);
  counter := !counter + 1
.....
Array.to_list arr

根据我的理解,它应该比第一种方法更好,但是,可能是因为我的代码中可能存在其他低性能错误,即使我更改了list,我仍然无法明确改善性能上述方法中的操作代码..

从理论上讲,这是我的问题,在上述三种策略中哪一种具有最佳性能?

我应该能够自己做一些实验,但作为一个更普遍的问题,在处理相关问题时是否有更好的绩效策略?

1 个答案:

答案 0 :(得分:2)

在列表末尾添加元素在列表的长度上是线性的,它确实需要复制整个列表。如果以这种方式构建整个列表,则会出现二次复杂性。

如果通过反转列表添加到最后,然后添加到前面,然后再次反转,则同样如此。撤消列表需要复制列表。

通常的技术是以相反的顺序构建列表完全,然后在完成后反转一次。这总体上只有线性复杂性。添加到列表的前面是一个恒定时间操作。

如果您不需要增加数组的大小(需要数组的副本),则在显示时使用数组也具有线性复杂性。

您的代码中可能还有其他慢点掩盖了您对这一点的更改。

在我看来,如果你没有大部分完成编码,你应该考虑基本的复杂性(线性,n log n,二次等)。如果您真的需要担心性能,可以稍后改进。你不想最终抛出你花了很多时间调整的代码。