Scala不可变Map慢

时间:2016-08-20 10:44:08

标签: java performance scala functional-programming bigdata

我在创建地图时有一段代码:

 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)

当我这样做的时候非常慢,超过1小时的2&000; 000&000; 000条目。

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

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

脚本运行的过程不到1分钟。对于2&000; 000和000,000,000个条目。

我进行了一些分析,看起来就像是在创建对象时,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` 

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

1 个答案:

答案 0 :(得分:3)

(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分钟进行10%GC活动(VisualVM),并因内存不足而死亡。

然而,当我尝试-Xmx4G时,两者都非常快。

P.S。 toMap所做的是重复向前缀树添加元素,因此每个元素都必须克隆(Array.copy):https://github.com/scala/scala/blob/99a82be91cbb85239f70508f6695c6b21fd3558c/src/library/scala/collection/immutable/HashMap.scala#L321

因此,toMap重复(2000000次)执行updated0,而Array.copy反过来经常执行-Xmx,这需要大量的内存分配(在低内存中) case)导致GC在大多数时间内进入MarkAndSweep(慢速垃圾收集)(据我所见,从jconsole)。

解决方案:是否增加内存(-Xms / temp1 JVM参数)或者如果您需要对数据集执行更复杂的操作,请使用Apache Spark(或任何面向批处理的map-reduce)框架)以分布式方式处理您的数据。

相关问题