位图矢量如何比普通矢量快?

时间:2012-01-13 01:15:31

标签: scala data-structures clojure functional-programming jvm

它是supposedly faster而不是矢量,但我真的不明白引用的位置应该如何帮助它(因为矢量根据定义可能是最本地打包的数据 - 每个元素都被打包在旁边后续元素,之间没有额外的空间。)

基准测试是否采用特定的使用模式或类似的东西?

这怎么可能?

6 个答案:

答案 0 :(得分:11)

位图向量尝试不是比正常向量严格更快,至少不是一切。这取决于你正在考虑的操作。

传统向量更快,例如,在访问特定索引处的数据元素时。直接索引数组查找很难打败。从缓存局部性的角度来看,如果您所做的只是按顺序循环遍历大数组,那么大数组就非常好了。

然而,位图矢量trie对于其他操作会更快(由于结构共享) - 例如,在不影响原始数据结构的情况下创建具有单个更改元素的新副本是O(log32 n)与O(n )对于传统的矢量。这是一个巨大的胜利。

这是一个非常值得关注这个主题的精彩视频,其中包括为什么您可能需要使用您的语言的这类结构的动机:Persistent Data Structures and Managed References(由Rich Hickey讲话)。

答案 1 :(得分:10)

其他答案中有很多好东西但是nobdy回答了你的问题。 PersistenVectors只能通过索引快速进行大量随机查找(当数组较大时)。 “怎么可能?”你可能会问。 “普通的平面阵列只需要移动指针,PersistentVector必须经过多个步骤。”

答案是“缓存局部性”。

缓存始终从内存中获取范围。如果你有一个大数组,它不适合缓存。因此,如果要获取项目x和项目y,则必须重新加载整个缓存。那是因为数组在内存中始终是连续的。

现在PVector与众不同。周围有许多小型数组,JVM很聪明,并且它们在内存中彼此靠近。所以对于随机访问,这很快;如果按顺序执行它会慢得多。

我不得不说我不是硬件方面的专家或者JVM如何处理缓存局部性我自己从未对此进行过基准测试;我只是在重述我从其他人那里听到的东西:)

编辑:迈克拉也提到了这一点。

编辑2:请参阅有关功能数据结构的讨论,如果您只对矢量感兴趣,请跳到最后一部分。 http://www.infoq.com/presentations/Functional-Data-Structures-in-Scala

答案 2 :(得分:6)

位图矢量trie(又名持久矢量)是Rich Hickey为Clojure发明的数据结构,自2010年以来已在Scala中实现(v 2.8)。正是它聪明的按位索引策略允许高效访问和修改大型数据集。

来自Understanding Clojure's Persistent Vectors

  

可变向量和ArrayLists通常只是增长的数组   并在需要时收缩。当你想要可变性时,这很有用   但是当你想要持久性时,这是一个大问题。你变慢了   修改操作,因为您必须复制整个数组   所有的时间,它会使用大量的内存。这将是理想的   以某种方式避免冗余尽可能不丢失   查找值时的性能以及快速操作。那   正是Clojure的持久向量所做的,并且已经完成了   通过平衡,有序的树木。

     

这个想法是实现一个类似于二进制的结构   树。唯一的区别是树中的内部节点有   最多两个子节点的引用,不包含任何元素   他们自己。叶节点最多包含两个元素。要素   按顺序,这意味着第一个元素是第一个元素   在最左边的叶子中,最后一个元素是最右边的元素   最右边的叶子。现在,我们要求所有叶节点都在   相同深度2。举个例子,看看下面的树:它有   其中的整数0到8,其中0是第一个元素,8是   持续。数字9是矢量大小:

     

enter image description here

     

如果我们想在这个向量的末尾添加一个新元素,我们   在可变世界中,我们会在最右边的叶子中插入9   节点,像这样:

     

enter image description here

     

但问题在于:如果我们想坚持下去,我们就不能这样做。   如果我们想要更新元素,这显然不会起作用!   我们需要复制整个结构,或至少部分结构。

     

为了在保持完全持久性的同时最小化复制,我们执行路径   复制:我们将路径上的所有节点复制到我们所关注的值   更新或插入,并在我们的时候用新的值替换值   在底部。多次插入的结果如下所示。这里,   具有7个元素的向量与具有10的向量共享结构   元素:

     

enter image description here

     

粉红色的节点在矢量之间共享,而   棕色和蓝色是分开的。其他未可视化的载体也可能   与这些向量共享节点。


更多信息

除了Understanding Clojure's Persistent Vectors之外,这个数据结构及其用例背后的想法也在David Nolen的2014年演讲Immutability, interactivity & JavaScript中得到了很好的解释,下面是截图。或者,如果您真的想深入了解技术细节,请参阅Phil Bagwell的Ideal Hash Trees,这是Hickey最初的Clojure实现所依据的论文。

Persistent bitmap trie

答案 3 :(得分:5)

“普通矢量”是什么意思?只是一个平面的项目?如果你永远不会更新它,那就太好了,但是如果你改变一个1M元素的平面向量,你必须进行大量的复制;存在树以允许您共享大部分结构。

答案 4 :(得分:2)

简短说明:它使用JVM在读/写/复制数组数据结构上进行优化的事实。 IMO的关键方面是,如果你的向量增长到一定的大小,索引管理就成了瓶颈。这里有一个非常聪明的算法,从持久的矢量到游戏,在非常大的集合上,它优于标准变体。所以基本上它是一个功能性数据结构,它只能很好地执行,因为它构建在小型可变高度优化的JVM数据结构上。 有关详细信息,请参阅此处(最后) http://topsy.com/vimeo.com/28760673

答案 5 :(得分:1)

从谈话的标题来看,它正在谈论 Scala 向量,这些向量甚至都不接近“可能最本地化的数据”:请参阅https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src/library/scala/collection/immutable/Vector.scala处的来源。 / p>

您的定义仅适用于Lisps(据我所知)。