Scala Language
Parallele Sammlungen
Suche…
Bemerkungen
Parallele Kollektionen erleichtern die parallele Programmierung, indem Parallelisierungsdetails auf niedriger Ebene ausgeblendet werden. Dies macht es einfach, Multi-Core-Architekturen zu nutzen. Beispiele für parallele Sammlungen umfassen ParArray
, ParVector
, mutable.ParHashMap
, immutable.ParHashMap
und ParRange
. Eine vollständige Liste finden Sie in der Dokumentation .
Parallele Sammlungen erstellen und verwenden
Um eine parallele Sammlung aus einer sequentiellen Sammlung zu erstellen, rufen Sie die par
Methode auf. Um eine sequentielle Sammlung aus einer parallelen Sammlung zu erstellen, rufen Sie die Methode seq
. Dieses Beispiel zeigt, wie Sie einen regulären Vector
in einen ParVector
und dann wieder zurückkehren:
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)
Die par
Methode kann verkettet werden, sodass Sie eine sequentielle Auflistung in eine parallele Auflistung konvertieren und sofort eine Aktion darauf ausführen können:
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)
In diesen Beispielen wird die Arbeit in mehrere Verarbeitungseinheiten aufgeteilt und nach Abschluss der Arbeit wieder zusammengefügt - ohne dass ein Eingreifen des Entwicklers erforderlich ist.
Fallstricke
Verwenden Sie keine parallelen Sammlungen, wenn die Sammlungselemente in einer bestimmten Reihenfolge empfangen werden müssen.
Parallele Sammlungen führen gleichzeitig Operationen durch. Das bedeutet, dass die gesamte Arbeit in Teile aufgeteilt und an verschiedene Prozessoren verteilt wird. Jeder Verarbeiter weiß nicht, welche Arbeit andere erledigen. Wenn die Reihenfolge der Sammlung von Belang ist, ist die parallel verarbeitete Arbeit nicht deterministisch. (Wenn Sie denselben Code zweimal ausführen, kann dies zu unterschiedlichen Ergebnissen führen.)
Nicht assoziative Operationen
Wenn eine Operation nicht assoziativ ist (wenn die Reihenfolge der Ausführung von Belang ist), ist das Ergebnis einer parallelisierten Sammlung nicht deterministisch.
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
Nebenwirkungen
Operationen, die Nebeneffekte haben, wie z. B. foreach
, werden bei parallelisierten Sammlungen aufgrund von foreach
möglicherweise nicht wie gewünscht ausgeführt. Vermeiden Sie dies, indem Sie Funktionen verwenden, die keine Nebenwirkungen haben, wie z. B. reduce
oder 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...