Scala Language
Collections parallèles
Recherche…
Remarques
Les collections parallèles facilitent la programmation parallèle en masquant les détails de la parallélisation de bas niveau. Cela facilite l'utilisation des architectures multicœurs. ParArray
, ParVector
, mutable.ParHashMap
, immutable.ParHashMap
et ParRange
exemples de collections parallèles. Une liste complète peut être trouvée dans la documentation .
Créer et utiliser des collections parallèles
Pour créer une collection parallèle à partir d'une collection séquentielle, appelez la méthode par
. Pour créer une collection séquentielle à partir d'une collection parallèle, appelez la méthode seq
. Cet exemple montre comment transformer un Vector
normal en un ParVector
, puis à nouveau:
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)
La méthode par
peut être chaînée, ce qui vous permet de convertir une collection séquentielle en une collection parallèle et d'y effectuer immédiatement une action:
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)
Dans ces exemples, le travail est en fait distribué à plusieurs unités de traitement, puis joint à nouveau une fois le travail terminé, sans intervention du développeur.
Pièges
N'utilisez pas de collections parallèles lorsque les éléments de la collection doivent être reçus dans un ordre spécifique.
Les collections parallèles effectuent des opérations simultanément. Cela signifie que tout le travail est divisé en plusieurs parties et distribué à différents processeurs. Chaque processeur n'est pas au courant du travail effectué par d'autres. Si l' ordre de la collecte est important, alors le travail traité en parallèle n'est pas déterministe. (Exécuter deux fois le même code peut donner des résultats différents.)
Opérations non associatives
Si une opération est non associative (si l'ordre d'exécution est important), le résultat sur une collection parallélisée sera non déterministe.
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
Effets secondaires
Les opérations qui ont des effets secondaires, tels que foreach
, peuvent ne pas s'exécuter comme souhaité sur des collections parallélisées en raison de conditions de concurrence. Évitez cela en utilisant des fonctions sans effets secondaires, telles que reduce
ou 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...