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