Scala 不可变映射缓慢



当我创建一个地图时,我有一段代码,如下所示:

 val map = gtfLineArr(8).split(";").map(_ split """).collect { case Array(k, v) => (k, v) }.toMap

然后我使用此映射来创建我的对象:

case class MyObject(val attribute1: String, val attribute2: Map[String:String]) 

我正在阅读数百万行并使用迭代器转换为 MyObjects。喜欢

MyObject("1", map)

当我这样做时,它真的很慢,2'000'000 个条目超过 1 小时。

我从对象创建中删除了地图,但我仍然执行拆分过程(第 1 节(:

val map = gtfLineArr(8).split(";").map(_ split """).collect { case Array(k, v) => (k, v) }.toMap
MyObject("1", null)

对于 2'000'000 个条目,脚本在不到 1 分钟的时间内运行。

我做了一些分析,看起来像是当创建对象时,val map到对象映射之间的分配使过程变慢。我错过了什么?

更新以更好地解释问题:

如果你看到我的代码来解释我的自我迭代超过 2000000 行,将每行转换为内部对象,迭代我这样做:

it.map(cretateNewObject).toList

这个迭代器遍历所有行,并使用函数 createNewObject 将它们转换为我的对象。

这实际上非常快,特别是使用dk14所说的大内存。 性能问题出在我的内部

`crateNewObject(val line:String)` 

此函数创建一个对象

`class MyObject(val attribute1:String, val attribute2:Map[String, String])` 

我的函数先行做

`val attributeArr = line.split("t")` 

数组的第一个属性记录是我的对象的 attribute1,第二个属性是

`val map = attributeArr(8).split(";").map(_ split """).collect { case Array(k, v) => (k, v) }.toMap` 

如果我只打印 map 中的元素数量,程序将在 2 分钟内结束,如果我将 map 传递给我的新对象行MyObject(attribute1, map)程序真的很慢。

如果您给它们足够的内存,(0 to 2000000).toList(0 to 2000000).map(x => x -> x).toMap具有相似的性能(我尝试了-Xmx4G - 4千兆字节(。 toMap实现很多是关于克隆的,所以很多内存被"分配"/"解除分配"。因此,在内存不足的情况下,GC变得过度活跃。

当我尝试以 128Mb 运行(0 to 2000000).toList时 - 它花了几秒钟,但(0 to 2000000).map(x => x -> x).toMap 2% 的 GC 活动 (VisualVM( 下至少花了 10 分钟,并且在内存不足的情况下死亡。

然而,当我尝试-Xmx4G两者都很快。


附言toMap所做的是反复将一个元素添加到前缀树中,所以它必须为每个元素克隆(Array.copy(很多:https://github.com/scala/scala/blob/99a82be91cbb85239f70508f6695c6b21fd3558c/src/library/scala/collection/immutable/HashMap.scala#L321。

因此,toMap 重复(2000000 次(执行updated0,这反过来又经常执行Array.copy,这需要大量的内存分配,这(在低内存情况下(导致 GC 大部分时间都去 MarkAndSweep(慢垃圾收集((据我从 jconsole 看到(。


解决方案:无论是增加内存(-Xmx/-Xms JVM参数(,还是需要对数据集进行更复杂的操作,请使用Apache Spark(或任何面向批处理的map-reduce框架(之类的东西以分布式方式处理数据。

最新更新