附加向量的效率

时间:2014-06-28 16:39:57

标签: performance scala vector clojure immutability

将元素追加到百万元素ArrayList上会产生现在设置一个引用的成本,以及将来必须调整ArrayList时复制一个引用的费用。

据我了解,将元素附加到百万元素PersistenVector上必须创建一个新路径,该路径由4个大小为32的数组组成。这意味着必须触及超过120个引用。

Clojure如何设法将矢量开销保持在"差2.5倍"或者" 4倍恶化" (与#34;差60倍"相反),我最近看到的几个Clojure视频中声称了这一点?它与缓存或引用的位置或我不知道的东西有关吗?

或者以某种方式在某种程度上可以使用变异在内部构建一个向量,然后在将它暴露给外界之前将其变为不可变?

我也标记了问题,因为scala.collection.immutable.vector基本上是相同的,对吗?

2 个答案:

答案 0 :(得分:9)

Clojure的PersistentVector具有特殊的尾部缓冲区,可以在向量的末尾实现高效的操作。只有在填充了这个32元素数组之后,才会将其添加到树的其余部分。这使摊销成本保持在较低水平。 Here是关于实施的一篇文章。 source也值得一读。

关于,“是否有可能在内部构建带有变异的载体,然后在向外界揭示它之前将其变为不可变的?”,是的!这些在Clojure中称为transients,用于有效的批量更改。

答案 1 :(得分:7)

无法讲述Clojure,但我可以对Scala Vectors进行一些评论。

持久性Scala向量(scala.collection.immutable.Vector s)在追加时比数组缓冲区慢。实际上,它们比List前置操作慢10倍。它们比追加到我们在并行集合中使用的Conc-tree慢2倍。

但是,Scala还有可变向量 - 它们隐藏在类VectorBuilder中。附加到可变向量不会保留向量的先前版本,而是通过将指针保持在向量中最右边的叶子来使其变异。所以,是的 - 在内部保持向量可变,而不是返回不可变引用正是在Scala集合中完成的。

VectorBuilder略快于ArrayBuffer,因为它只需要分配一次数组,而ArrayBuffer需要平均两次(因为增长)。我们用作并行数组合器的Conc.Buffer s的速度是VectorBuilder的两倍。

基准在这里。没有任何基准涉及任何拳击,它们与参考对象一起使用以避免任何偏见:

更多收藏基准here

这些测试是使用ScalaMeter执行的。