【问题标题】:Why coping/updating list are very slow in OCaml?为什么 OCaml 中的应对/更新列表非常慢?
【发布时间】:2014-05-21 19:50:46
【问题描述】:

所以基本上我在我的 ocaml 程序中使用列表,最初它是这样的:

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

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

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

我还以为是因为上面代码中有一个整个列表复制的过程,(每次ll &lt;- 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操作代码,我仍然无法显式提高性能在上述方法中..

所以我的问题是,理论上,在上述三种策略中,哪一种表现最好?

我自己应该可以做一些实验,但是作为一个更笼统的问题,在处理相关问题上有没有更好的性能策略?

【问题讨论】:

  • “所以我以这种方式更改了我的代码:”要么你解释错了,而且你正在对 List.rev 进行 N 次调用,正如 Jeffrey 似乎假设的那样。或者您执行了错误的操作并且您没有测试此代码(编译器是否可能发出错误并让您执行以前的版本?)。 ll &lt;- x::ll 是恒定时间且非常便宜。
  • 嗨@PascalCuoq,是的,我解释错了,我将头部延长了N次然后做了一次List.rev

标签: arrays performance linked-list ocaml


【解决方案1】:

在列表末尾添加一个元素与列表长度呈线性关系,并且确实需要复制整个列表。如果以这种方式构建整个列表,则会得到二次复杂度。

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

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

如果您不需要增加数组的大小(这需要数组的副本),那么使用您展示的数组也具有线性复杂性。

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

在我看来,如果您大部分时间没有完成编码,您应该只考虑基本复杂性(线性、n log n、二次等)。如果您真的需要担心性能,您可以稍后改进。您不希望最终丢弃花费大量时间调整的代码。

【讨论】:

  • 您好杰弗里,非常感谢您的回答!抱歉,我解释错了,我将头部延长了 N 次,然后做了一次 List.rev 并且我已经对其进行了测试,它可以在非常低的性能下正常工作......它应该在我的代码的其他部分......
猜你喜欢
  • 1970-01-01
  • 2021-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-29
  • 2020-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多