2012-02-09 28 views
3

因此,对于作业任务,我应该使用几个线程机制,使用一个应该导致pi的函数的简单集成。该实施应该处理超过500亿美元的时间间隔。我目前的实现处理2GB堆大小约5000万的for循环。现在我的问题是为什么实现使用这么多的内存? (我认为这是因为范围必须提前制定,这是真的吗?)我该如何改善记忆体的使用?是否有可能使用并行集合,或者我是否被迫使用线程池来处理这样的事情?斯卡拉并行用尽RAM

注意:以下实现将得到充分的评价。这是为了我的智力好奇心和我在Scala中变得更流利的梦想。

import scala.Math 

object Pi { 
def calculate_pi(interval: Int): Double = { 
    val start: Long = System.currentTimeMillis; 
    var duration: Long = 0 
    var x: Double = 2.0/interval 
    var y: Double = 0.0 
    var mypi: Double = 0.0 

    (x until 1.0 by 1.0/interval).par.foreach(i => { 
     y = 4.0/(1.0+x*x) 
     mypi += (1.0/interval)*y 
    }) 

    duration = (System.currentTimeMillis - start) 
    println("scala calculate_pi\t" + interval + "\t" + duration + "ms\t" + mypi) 
    return mypi 
} 




object Main extends App { 
    println("Please input the interval for the calculation") 
    if(args.length < 1) { sys.exit } 
    val interval = args(0).toInt 
    Pi.calculate_pi_seq(interval) 
    Pi.calculate_pi(interval) 
} 
+5

它甚至工作吗?看起来你正在同时修改mypi。而且,更平凡但更重要的是,你不使用你的循环变量我(你正在使用你的常量x) – 2012-02-09 08:23:14

+0

你可以发布你得到的OutOfMemoryError吗? – axel22 2012-02-09 09:20:50

+0

java.lang.OutOfMemoryError:Java堆空间 – Bbatha 2012-02-10 00:15:24

回答

6

这是各种错误的:

(x until 1.0 by 1.0/interval).par.foreach(i => { 
    y = 4.0/(1.0+x*x) 
    mypi += (1.0/interval)*y 
}) 

的第一个问题是,y所有的计算是一致的:不使用i在计算过程中。由于x不会更改,所有线程计算相同的值。

这里是第二个问题,您可以并行计算mypi(和y。这意味着多个线程正在同时读取和写入mypiy

让我们考虑一个执行来理解这个问题。假设第一个线程开始运行,计算y,然后读取ymypi。该线程然后暂停,并且所有其他线程运行。最后,该线程继续并将其计算结果写入mypi。在这种情况下,所有其他线程的所有计算都被浪费了,因为最终值由该线程给出。

这是一个简单的例子。基本上,根本不能预测每个读取和写入mypi会发生什么(y更容易,因为所有线程都为其分配相同的值)。

而且,当您在NumericRange上致电.par时,它会创建一个集合,其中包含所有NumericRange的值。

+0

感谢x * x在我翻译C顺序文件时没有注意。我不确定为什么修改mypi并行应该是重要的,如果我没有弄错的话,整数的加法是原子的。很明显,这通常不是好的做法,但我认为在简单的情况下这应该是可以的。有没有一个建议可以解决这个问题,即我应该将所有中间结果存储在一个列表中并对其进行总结? – Bbatha 2012-02-10 00:10:05

+0

读int是原子,写int是原子。读取 - 修改 - 写入不是原子的。 (查看丢失的更新。)第二个问题是mypi可以在本地缓存线程。 (请参阅java关键字volatile。)一种选择是使用map-reduce算法。 – user482745 2012-02-29 09:07:06

-3

不知道底层的应用,我已经通过实验证明,如果你使用的方法上Rangepar(例如)它提前被实例化,正如你所指出的教训。

但是,它看起来像只使用集合来利用并行化。换句话说,要计算一段与集合本身无关的代码 - 值i甚至没有被使用。因此,foreach循环非常多余,因为您只感兴趣的是y和x值。对于一个简单的for-loop可以完成的事情来说,这看起来像是一项大量的工作。

表示其他类型的并行化在scala中非常容易。怎么样使用演员?它们轻巧而且非常简单。否则工作线程或甚至Java线程可能会有所斩获。

+0

呃,什么? 'Range'不是一个“集合”,不管是“1到2”还是“1到1000000”都不会有更多的内存。我甚至不明白“仅使用集合来利用并行化”的含义:看起来循环对我来说是非常重要的算法! – 2012-02-09 11:37:31

+0

当然它是:)它只是一个集合,从一个值到另一个值。虽然我很遗憾不幸选择了单词,但我的意思是,如果使用方法par,那么将导致scala运行并计算集合中的每个元素(Range或Stream中不是这种情况) 。关于“使用集合来利用并行化”,我想这很明显,当方法par()被投入使用时。 Par就是简单的scalas集合,它提供了创建线程的智能替代方案。因此集合的“优势”。 – 2012-02-09 12:09:17