【问题标题】:Scala - Lazyness of Iterator and Iterable - Memory consumptionScala - Iterator 和 Iterable 的惰性 - 内存消耗
【发布时间】:2014-01-01 01:59:48
【问题描述】:

好的,我正在使用 dbpedia 处理英文维基百科转储。到目前为止,他们的实现扩展了Traversable 并提供了一个foreach 来检查转储。但是,我想要典型的地图操作,例如mapgrouped等。这是我打开的问题:https://github.com/dbpedia/extraction-framework/issues/140

所以我添加了一个 getter 来接收一个可迭代的和一个迭代器。现在有趣的部分:

source.iterable
      .map(parser)
      .zipWithIndex
      .map { case(page: PageMode, i: Int) =>
                 if(i%1000 == 0){println(i)}
                 (...)
            }
      .grouped(2000)

上面的代码内存不足。然而:

source.iterator
      .map(parser)
      .zipWithIndex
      .map { case(page: PageNode, i: Int) =>
                 if(i%1000 == 0){println(i)}
                 (...)
            }
      .grouped(2000)

此代码按预期立即返回。

在我看来,一旦内存耗尽,第一个示例就会完全运行代码,因为它试图将转储存储在内存中。后者没有。但是,后者返回的是 Seq 上的迭代器,而不是迭代器上的迭代器。

这是一个可迭代类的预期还是我做错了什么。我希望它们都立即返回并仅在迭代后才消耗内存。

感谢您的帮助! 卡斯滕

【问题讨论】:

标签: scala iterator iterable


【解决方案1】:

默认情况下,Scala 中的所有集合(流和视图除外)都是严格的,因此每个函数都在集合之上:

pages
  .map(parser)
  .zipWithIndex
  .map { partialFunction }

将返回新集合。您可以使用视图避免一些中间结果,然后将其强制返回到您的集合类型:

pages.view
  .map(parser)
  .zipWithIndex
  .map { partialFunction }
  .force

更多详情http://www.scala-lang.org/docu/files/collections-api/collections_42.html

【讨论】:

  • 我现在有这个:pages.iterable.view.map( parser ).zipWithIndex.map { ... } .grouped(5000).foreach { ... } 它可以创建。因为我只是立即迭代,所以我不强迫。这是一个好的做法吗?
  • 好问题,谢谢。 FOREACH 是为具有副作用的计算而设计的,因此它不会产生新的集合作为结果,而是修改现有的实体。所以我建议你在 FOREACH 之前做任何一个 FORCE。它看起来更一致。
  • 嗯,我已经读过几次了。但是,在我的情况下,foreach 只是保存到数据库中,因此没有真正的副作用。我会试试看它是否有效。
  • 我们可以肯定地说数据库操作是副作用。实际上,您在view 之后的链中指定的所有功能只有在调用force 之后才会应用。所以要小心结果。
【解决方案2】:

调用iterable 返回一个Iterable,这意味着一个集合有一个iterator 方法。所以:

  • source.iterable 返回一个可迭代的集合,它可能完全保存在内存中,也可能不完全保存在内存中
  • 但随后mapzipWithIndexmapgrouped 都会产生中间集合

另一方面,打电话给iterator

  • source.iterator 返回一个 Iterator 覆盖可能完全或可能不完全在内存中的内容
  • 那么mapzipWithIndexmapgrouped 将不会创建中间集合(它们会创建新的迭代器)

在我看来,这解释了为什么第一个示例更容易耗尽内存。

【讨论】:

  • 我实现了这两种方法。但是,我注意到您描述的行为。我认为mapgrouped 等不会为和iterable 创建中间集合。但是,view 如上所述避免了这种情况。我只是想知道在没有结论的情况下使用view 是否可以force。它对我有用,但我注意到 Scala 通常有正确的做事方式。
  • 关于 Scala 视图,另见answer by @Daniel C. Sobral here
猜你喜欢
  • 2016-08-10
  • 1970-01-01
  • 2012-04-06
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多