0

这是我昨天与我的学习小组分享的一些代码:https ://gist.github.com/natemurthy/019e49e6f5f0d1be8719 。编译后,我使用以下堆参数运行 map.scala:

$ scala -J"-Xmx4G" map

并获得以下 4 次单独测试的结果:

// (1L to 20000000L).map(_*2)
(Map) multiplying 20 million elements by 2
(Reduce) sum: 400000020000000
Total MapReduce time: 7.562381

// (1L to 20000000L).toArray.map(_*2)
(Map) multiplying 20 million elements by 2
(Reduce) sum: 400000020000000
Total MapReduce time: 1.233997

// (1L to 20000000L).toVector.map(_*2)
(Map) multiplying 20 million elements by 2
(Reduce) sum: 400000020000000
Total MapReduce time: 15.041896 

// (1L to 20000000L).par.map(_*2)
(Map) multiplying 20 million elements by 2
(Reduce) sum: 400000020000000
Total MapReduce time: 18.586220

我试图弄清楚为什么这些结果在不同的集合类型中会有所不同,更重要的是,为什么对于应该直观地更快评估的集合来说性能似乎更差。很想听听您对这些结果的见解。我还尝试在Breeze和 Saddle 上执行这些操作(在相同的测试中表现更好),但我想看看我可以将内置的 Scala Collections API 推到多远。

这些测试在华硕 Zenbook UX31A、英特尔酷睿 i7 3517U 1.9 GHz 双核超线程、4 GB RAM 和 Ubuntu 12.04 桌面上运行。将 Scala 2.11.1 与 JDK 1.7 一起使用

4

1 回答 1

5

显然这里发生了很多事情,但这里有一些:

首先,该to方法创建一个Range,这是一个非常有效的数据结构,因为它实际上并没有创建一个包含 2000 万个元素的集合。它只知道如何在迭代时获取下一个元素。当您调用maponRange时,输出是 a Vector,因此它遍历 Range(便宜),将每个数字乘以 2(仍然便宜),但随后必须创建 a Vector(昂贵;我猜大约 7.5 秒)。

其次,当您调用.toVectoraRange时,它必须实际创建 aVector并生成所有这 2000 万个值。这需要时间(同样是 7.5 秒)。当您调用时map,它会遍历 Vector(便宜),将每个数字乘以 2(仍然便宜),但随后必须为结果创建一个 Vector的(昂贵)。因此,您执行了相同的操作,但这次创建了两个包含 2000 万个元素的新向量。(7.5*2=15 秒。)

第三,数组是非常简单的数据结构,开销极低。它们的创建、索引和插入速度很快,因此构造一个大数组,然后对其进行映射以将元素插入到新数组中,速度也很快。

最后,.paron 调用Range产生一个ParRange. 结果map是 a ParVector,因此创建该对象并将 2000 万个元素放入其中是有成本的。当您调用.map它时,它会创建线程来执行计算。但是,您正在映射的操作非常快,因此并行执行它确实没有任何好处。与实际计算乘法相比,您花在处理并行化开销上的时间要多得多。

这样想吧。如果你想在现实生活中做这个操作,你会召集一群朋友做你的线程。然后你必须把你的 2000 万个数字除以给每个朋友几个来做乘法运算。然后你的朋友会将每个数字乘以 2,然后返回加倍的数字,然后等待你分发下一组数字。然后你必须将每个相乘的数字输入到一个新表中。但是将一个数字乘以 2 的任务非常快,以至于您自己完成它所花费的时间比您将朋友聚在一起并来回传递消息所花费的时间还短。此外,如果你只有两个核心,那无论如何都不是并行化的空间,所以只有几个线程同时工作,

于 2014-07-16T18:44:36.460 回答