Странное поведение с использованием параллельных коллекций Scala и setParallelism - PullRequest
1 голос
/ 02 сентября 2011

Я недавно узнал о Parallel Collection в Scala 2.9 и был взволнован, увидев, что степень параллелизма можно установить с помощью collection.parallel.ForkJoinTasks.defaultForkJoinPool.setParallelism.

Однако, когда я попробовал экспериментдобавив два вектора размером один миллион каждый, я нахожу

  • Использование параллельного сбора с параллелизмом, установленным на 64, так же быстро, как и последовательный (показано в результатах).
  • Увеличение setParallelism, похоже, увеличиваетсяпроизводительность нелинейным способом.Я бы, по крайней мере, ожидал монотонное поведение (то есть производительность не должна ухудшаться, если я увеличил параллелизм)

Может кто-нибудь объяснить, почему это происходит

  object examplePar extends App{    

  val Rnd = new Random()
  val numSims = 1

  val x = for(j <- 1 to 1000000) yield Rnd.nextDouble()
  val y = for(j <- 1 to 1000000) yield Rnd.nextDouble()

  val parInt = List(1,2,4,8,16,32,64,128,256)    
  var avg:Double = 0.0
  var currTime:Long = 0

  for(j <- parInt){
    collection.parallel.ForkJoinTasks.defaultForkJoinPool.setParallelism(j)
    avg = 0.0
    for (k <- 1 to numSims){
      currTime = System.currentTimeMillis()    
      (x zip y).par.map(x => x._1 + x._2)
      avg += (System.currentTimeMillis() - currTime)
    }
    println("Average Time to execute with Parallelism set to " + j.toString + " = "+ (avg/numSims).toString + "ms")    
  }

  currTime = System.currentTimeMillis()
  (x zip y).map(x => x._1 + x._2)
  println("Time to execute using Sequential = " + (System.currentTimeMillis() - currTime).toString + "ms")        
}

Результаты запускапример использования Scala 2.9.1 и четырехъядерного процессора:

Average Time to execute with Parallelism set to 1 = 1047.0ms
Average Time to execute with Parallelism set to 2 = 594.0ms
Average Time to execute with Parallelism set to 4 = 672.0ms
Average Time to execute with Parallelism set to 8 = 343.0ms
Average Time to execute with Parallelism set to 16 = 375.0ms
Average Time to execute with Parallelism set to 32 = 391.0ms
Average Time to execute with Parallelism set to 64 = 406.0ms
Average Time to execute with Parallelism set to 128 = 813.0ms
Average Time to execute with Parallelism set to 256 = 469.0ms
Time to execute using Sequential = 406ms

Хотя эти результаты относятся к одному прогону, они согласуются при усреднении по нескольким прогонам

Ответы [ 2 ]

2 голосов
/ 02 сентября 2011

Параллелизм не приходит бесплатно. Требуются дополнительные циклы, чтобы разбить проблему на более мелкие куски, упорядочить все и синхронизировать результат.

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

В вашем тестовом примере вы добавляете два типа double, что является тривиальным упражнением и занимает так мало времени, что накладные расходы при распараллеливании больше, чем простое выполнение задачи в одном потоке.

Опять-таки, по аналогии можно позвонить всем своим друзьям, чтобы помочь вам переместить 3 чемодана. Это займет у вас полдня, чтобы избавиться от них, в то время как вы можете закончить самостоятельно в считанные минуты.

Чтобы получить какую-либо выгоду от распараллеливания, ваша задача должна быть достаточно сложной, чтобы гарантировать дополнительные издержки. Попробуйте выполнить некоторые дорогостоящие вычисления, например, формулу, включающую сочетание 5-10 тригонометрических и логарифмических функций.

0 голосов
/ 02 сентября 2011

Я бы предложил изучить и использовать черту scala.testing.Benchmark для сравнения фрагментов кода.При тестировании JVM необходимо учитывать JIT, GC и другие вещи - см. этот документ .Короче говоря, вы должны выполнять каждый из прогонов в отдельной JVM после выполнения нескольких прогонов прогона.

Также обратите внимание, что часть (x zip y) не выполняется параллельно, потому что xи y еще не параллельны - zip выполняется последовательно.Затем я бы предложил превратить x и y в массивы (toArray) и затем вызвать par - это обеспечит использование эталонным тестом параллельных массивов, а не параллельных векторов (которые медленнее для методов преобразования, таких какzip и map).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...