Scala Language
Параллельные коллекции
Поиск…
замечания
Параллельные коллекции облегчают параллельное программирование, скрывая детали параллелизма низкого уровня. Это упрощает использование многоядерных архитектур. Примеры параллельных коллекций включают ParArray
, ParVector
, mutable.ParHashMap
, immutable.ParHashMap
и ParRange
. Полный список можно найти в документации .
Создание и использование параллельных коллекций
Чтобы создать параллельную коллекцию из последовательной коллекции, вызовите метод par
. Чтобы создать последовательный сбор из параллельной коллекции, вызовите метод seq
. В этом примере показано, как вы превращаете обычный Vector
в ParVector
, а затем снова:
scala> val vect = (1 to 5).toVector
vect: Vector[Int] = Vector(1, 2, 3, 4, 5)
scala> val parVect = vect.par
parVect: scala.collection.parallel.immutable.ParVector[Int] = ParVector(1, 2, 3, 4, 5)
scala> parVect.seq
res0: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 5)
Метод par
может быть привязан, что позволяет конвертировать последовательную коллекцию в параллельную коллекцию и немедленно выполнять действие над ней:
scala> vect.map(_ * 2)
res1: scala.collection.immutable.Vector[Int] = Vector(2, 4, 6, 8, 10)
scala> vect.par.map(_ * 2)
res2: scala.collection.parallel.immutable.ParVector[Int] = ParVector(2, 4, 6, 8, 10)
В этих примерах работа фактически распределяется по нескольким модулям обработки, а затем снова объединяется после завершения работы без вмешательства разработчика.
Ловушки
Не используйте параллельные коллекции, когда элементы коллекции должны быть получены в определенном порядке.
Параллельные коллекции выполняют операции одновременно. Это означает, что вся работа делится на части и распространяется на разные процессоры. Каждый процессор не знает о работе, выполняемой другими. Если порядок сбора имеет значение, то работа, обрабатываемая параллельно, недетерминирована. (Выполнение того же кода дважды может дать разные результаты.)
Неассоциативные операции
Если операция неассоциативна (если порядок выполнения имеет значение), то результат в распараллеленном наборе будет недетерминированным.
scala> val list = (1 to 1000).toList
list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10...
scala> list.reduce(_ - _)
res0: Int = -500498
scala> list.reduce(_ - _)
res1: Int = -500498
scala> list.reduce(_ - _)
res2: Int = -500498
scala> val listPar = list.par
listPar: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10...
scala> listPar.reduce(_ - _)
res3: Int = -408314
scala> listPar.reduce(_ - _)
res4: Int = -422884
scala> listPar.reduce(_ - _)
res5: Int = -301748
Побочные эффекты
Операции, которые имеют побочные эффекты, такие как foreach
, могут не выполняться по желанию при параллельных сборах из-за условий гонки. Избегайте этого, используя функции, которые не имеют побочных эффектов, таких как reduce
или map
.
scala> val wittyOneLiner = Array("Artificial", "Intelligence", "is", "no", "match", "for", "natural", "stupidity")
scala> wittyOneLiner.foreach(word => print(word + " "))
Artificial Intelligence is no match for natural stupidity
scala> wittyOneLiner.par.foreach(word => print(word + " "))
match natural is for Artificial no stupidity Intelligence
scala> print(wittyOneLiner.par.reduce{_ + " " + _})
Artificial Intelligence is no match for natural stupidity
scala> val list = (1 to 100).toList
list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15...