【问题标题】:Immutable Map implementation for huge maps大地图的不可变地图实现
【发布时间】:2010-02-19 16:49:36
【问题描述】:

如果我有一个 不可变 地图,我可能会期望(在很短的时间内 - 比如几秒钟)添加/删除 数十万项目来自,标准HashMap 是个坏主意吗?假设我想在不到 10 秒的时间内通过 Map 传递 1Gb 的数据,使得 Map 在任何时刻的最大大小仅为 256Mb。

我的印象是地图保留了某种“历史”,但我将始终访问最后更新的表格(即我不传递地图),因为它是私有的Actor 的成员变量,只能从反应内部更新/访问。

基本上我怀疑这种数据结构在短时间内读取大量数据时可能(部分)出现错误for issues I am seeing around JVMs going out of memory

使用不同的地图实现会更好吗?如果是,它是什么?

【问题讨论】:

    标签: scala map immutability out-of-memory scala-2.7


    【解决方案1】:

    哎哟。为什么必须使用不可变映射?可怜的垃圾收集器!除了 (log n) 时间之外,不可变映射通常每个操作需要 (log n) 个新对象,或者它们实际上只是将可变哈希映射和层变更集包装在顶部(这会减慢速度并增加对象创建的数量)。

    不变性很棒,但在我看来,现在不是使用它的时候。如果我是你,我会坚持使用scala.collection.mutable.HashMap。如果您需要并发访问,请将 Java util.concurrent 包装起来。

    您可能还想增加 JVM 中年轻代的大小:-Xmn1G 或更多(假设您使用 -Xmx3G 运行)。另外,使用吞吐量(并行)垃圾收集器。

    【讨论】:

    • 是的 - 我已将其更改为使用可变 Map 但我认为 FP 的全部意义在于不变性很棒!从“它在任何一个时间点真正需要多少数据”的角度来看,这个应用程序应该可以轻松地在不到 256Mb 的内存中运行。
    • 不变性的好坏取决于应用程序。如果您正在运行一个应用程序,例如,消息线程树被发送到一堆客户端,不变性是天赐之物——您只需发送当前树,您不必担心数据结构本身会发生变化从你下面。 (您仍然必须捕获诸如客户要求向线程添加评论的情况,该评论在他们响应时被删除。)对于快速翻转的私有数据结构中的高吞吐量工作,不变性提供的优势很少,而且需要大量开销。
    • 是的,这就是我发现的!那么 Haskell 或 Clojure 如何在这些情况下进行管理?他们不是强制不变性吗?
    • 在 FP 中,人们更喜欢基于有序、平衡树的映射,而不是基于哈希表的映射 - 正是因为很难获得它的持久版本。据我了解,FP 的人很快就放弃了哈希表的 O(1) 查找来寻找平衡树的 O(logn),其推理基本上相当于“谁关心 那个 的性能,它不没关系,我更喜欢不变性”。好吧,有些人只是更喜欢性能:)
    • AFAICT,Clojure 在需要可变数据结构时使用 Java 的集合。 Haskell 使用最好的可用不可变算法,然后将偶尔的 log N 视为值得牺牲的纯度。
    【解决方案2】:

    那太糟糕了。你说你总是想访问最后更新的表,也就是说你只需要一个 ephemeral 数据结构,不需要为 persistent 数据结构付出代价- 这就像交易时间和记忆来获得完全有争议的“风格点”。你没有在不需要时使用盲目的持久结构来建立你的业力。

    此外,哈希表是一种特别难以持久化的结构。换句话说,“非常非常慢”(基本上它在读取大大超过写入时可用 - 而且您似乎在谈论许多写入)。

    顺便说一句,ConcurrentHashMap 在这种设计中没有意义,因为地图是从单个参与者访问的(这是我从描述中理解的)。

    【讨论】:

    • 你是对的 - 我对不变性或并发性没有要求。我的地图是单个演员私有的缓存
    • 我假设,由于您正在使用参与者,您希望利用并发机会。这个 map/actor,现在听起来像是一个潜在的瓶颈,可能是一个很好的机会(尽管与 actor 无关),即您可能会通过将映射设为共享的 ConcurrentHashMap(不属于任何单个 actor)并让编写者同时进行而受益,如果可能/合理的话。
    • 数据是地图,不需要在 Actor 之间共享 - 大约有 30 个 Actor 实例,每个都有自己的地图(仅与它们相关的数据)
    • 我经常听到 Scala 中不可变映射性能问题的解决方案是“变可变”......当在函数式中坚持不可变数据结构时,您不能谈论“风格点”编程,它比这更基础和重要。要么我们可以做到并且确实存在不同的编程范式,要么我们必须每次都对自己进行第二次猜测,因此这里没有真正可行的新编程范式。
    【解决方案3】:

    Scala 所谓的 (*) 不可变 Map 在 Scala 2.7 之前已经超出了基本用法。不要相信我,只需查看它的开放票数即可。解决方案只是“它将在 Scala 2.8 上被其他东西取代”(确实如此)。

    因此,如果您想要 Scala 2.7.x 的不可变映射,我建议您在 Scala 以外的其他地方寻找它。或者直接使用 TreeHashMap。

    (*) Scala 的不可变 Map 并不是真正不可变的。它内部是一个可变数据结构,需要大量同步。

    【讨论】:

    • 我在哪里可以找到替代方案? TreeMap 好吗?我在 Java 中几乎找不到一个
    • @oxbow 我认为人们确实在使用HashTreeMap 作为替代品,既然你提到了它。
    • Scala 的不可变映射最近似乎已修复。该代码现在是一个 trie,旧的日志记录方案似乎已经消失了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多