Параллель Scala для нехватки оперативной памяти - PullRequest
3 голосов
/ 09 февраля 2012

Так что для домашнего задания я должен поиграть с несколькими механизмами потоков, используя простую интеграцию функции, которая должна привести к пи.Реализация должна обрабатывать интервал более 500 миллиардов.Моя текущая реализация обрабатывает цикл for примерно до 50 миллионов при размере кучи 2 ГБ.Теперь мой вопрос: почему реализация использует так много памяти?(Я думаю, это потому, что диапазон должен быть сделан заранее, это правда?) И как мне улучшить использование памяти?Можно ли делать с параллельными коллекциями или я вынужден использовать пул потоков для чего-то вроде этого?

Примечание: я получу полный кредит со следующей реализацией.Это для моего интеллектуального любопытства и моей мечты стать более свободным в скале.

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)
}

Ответы [ 2 ]

6 голосов
/ 09 февраля 2012

Это все виды неправильно:

(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 не изменяется, все потоки вычисляют одно и то же значение.

И вот вторая проблема, вы вычисляете mypiy) параллельно . Это означает, что несколько потоков одновременно читают и пишут mypi и y .

Давайте рассмотрим одно выполнение, чтобы понять проблему в этом. Допустим, первый поток запускается, вычисляет y, а затем читает y и mypi. Затем этот поток приостанавливается, и все остальные потоки запускаются. Наконец, этот поток возобновляет и записывает результат своего вычисления в mypi. В этом случае все вычисления всех других потоков тратятся впустую, поскольку окончательное значение было задано этим одним потоком.

Это был простой случай. По сути, вы вообще не можете предсказать, что произойдет для каждого из этих операций чтения и записи в mypi (y проще, поскольку все потоки присваивают ему одно и то же значение).

И, да, когда вы вызываете .par на NumericRange, он создает коллекцию со всеми значениями этого NumericRange.

0 голосов
/ 09 февраля 2012

Не зная базового приложения, я узнал из экспериментов, что если вы используете метод par на Range (например), он будет создан заранее, как вы указали.

Однако похоже, что вы используете коллекции только для того, чтобы воспользоваться преимуществами распараллеливания.Другими словами, для вычисления фрагмента кода, который несколько не связан с самой коллекцией - значение i даже не используется.Таким образом, цикл foreach является в значительной степени избыточным, поскольку вас интересуют только значения y и x.Может показаться, что для выполнения простого цикла for требуется большой объем работы.

При этом другие типы распараллеливания в scala довольно просты.Как насчет использования актеров?Они легкие и очень простые.В противном случае рабочие потоки или, может быть, даже потоки Java могут добиться цели.

...