将一个算术运算映射到一个Scala集合,并对结果求和



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

$ 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双核、超线程、4gb内存、Ubuntu 12.04 Desktop上运行的。使用Scala 2.11.1和JDK 1.7

显然这里有很多事情要做,但这里有几个:

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

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

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

最后,.parRange的调用产生ParRangemap的结果是ParVector,因此创建该对象并将2000万个元素放入其中是有代价的。当您调用.map时,它会创建线程来执行计算。然而,你要映射的操作非常快,所以并行处理并没有什么好处。处理并行化的开销比实际计算乘法花费的时间要多得多。

这样想。如果你想在现实生活中做这个操作,你会找一群朋友来做你的线程。然后你必须把你的2000万个数字分开,给每个朋友几个数字来做乘法。然后你的朋友会把每个数字乘以2,然后把翻倍的数字还给你,然后等你分发下一组数字。然后,必须将每个相乘的数字输入到一个新表中。但是,把一个数乘以2的任务是如此之快,以至于你可以用更少的时间来完成它,而不是麻烦地把你的朋友聚集在一起,来回传递信息。此外,如果您只有两个内核,那么无论如何都没有并行化的空间,因此只有几个线程同时工作,并且您的开销与工作比不是那么好。

相关内容

  • 没有找到相关文章

最新更新