scala: параллельные коллекции не работают?

Я пытаюсь использовать параллельные коллекции очень простым способом через.par - я ожидаю, что с коллекцией будут действовать не по порядку, но это не так:

scala> (1 to 10) map println
1
2
3
4
5
6
7
8
9
10

а также

scala> (1 to 10).par map println
1
2
3
4
5
6
7
8
9
10

похоже, что в последнем случае порядок не должен быть последовательным. это со scala 2.9, у моей машины 2 ядра. это где-то неправильная конфигурация? Спасибо!

редактировать: я действительно пробовал работать с большим набором (100 КБ), и результат все еще был последовательным.

2 ответа

Решение

YMMV:

scala> (1 to 10).par map println
1
6
2
3
4
7
5
8
9

Это на двухъядерном тоже...

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

import collection.parallel._
import collection.parallel.immutable._

class ParRangeEx(range: Range) extends ParRange(range) {
  // Some minimal number of elements after which this collection 
  // should be handled sequentially by different processors.
  override def threshold(sz: Int, p:Int) = {
    val res = super.threshold(sz, p)
    printf("threshold(%d, %d) returned %d\n", sz, p, res)
    res
  }
  override def splitter = {
    new ParRangeIterator(range) 
        with SignalContextPassingIterator[ParRangeIterator] {
      override def split: Seq[ParRangeIterator] = {
        val res = super.split
        println("split " + res) // probably doesn't show further splits
        res
      }
    }
  }
}

new ParRangeEx((1 to 10)).par map println

В некоторых прогонах я получаю перемежающуюся обработку, в некоторых прогонах - последовательную обработку. Кажется, разделить нагрузку на две части. Если вы измените возвращенное пороговое число на 11, вы увидите, что рабочая нагрузка никогда не будет разделена.

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

Вот пример вывода на моем компьютере:

threshold(10, 2) returned 1
split List(ParRangeIterator(over: Range(1, 2, 3, 4, 5)), 
  ParRangeIterator(over: Range(6, 7, 8, 9, 10)))
threshold(10, 2) returned 1
threshold(10, 2) returned 1
threshold(10, 2) returned 1
threshold(10, 2) returned 1
threshold(10, 2) returned 1
6
7
threshold(10, 2) returned 1
8
1
9
2
10
3
4
5

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

Другие вопросы по тегам