Scala Language
Colecciones paralelas
Buscar..
Observaciones
Las colecciones paralelas facilitan la programación paralela al ocultar los detalles de paralelización de bajo nivel. Esto hace que tomar ventaja de las arquitecturas de múltiples núcleos sea fácil. Los ejemplos de colecciones paralelas incluyen ParArray
, ParVector
, mutable.ParHashMap
, immutable.ParHashMap
y ParRange
. Una lista completa se puede encontrar en la documentación .
Creación y uso de colecciones paralelas
Para crear una colección paralela a partir de una colección secuencial, llame al método par
. Para crear una colección secuencial a partir de una colección paralela, llame al método seq
. Este ejemplo muestra cómo convertir un Vector
regular en un ParVector
y luego de nuevo:
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)
El método par
se puede encadenar, lo que le permite convertir una colección secuencial en una colección paralela y realizar una acción de inmediato:
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)
En estos ejemplos, el trabajo en realidad se divide en varias unidades de procesamiento y luego se vuelve a unir después de que el trabajo se completa, sin la intervención del desarrollador.
Escollos
No utilice colecciones paralelas cuando los elementos de la colección deben recibirse en un orden específico.
Las colecciones paralelas realizan operaciones concurrentemente. Eso significa que todo el trabajo se divide en partes y se distribuye a diferentes procesadores. Cada procesador desconoce el trabajo realizado por otros. Si el orden de la colección importa, entonces el trabajo procesado en paralelo no es determinista. (Ejecutar el mismo código dos veces puede producir resultados diferentes).
Operaciones no asociativas
Si una operación no es asociativa (si el orden de ejecución es importante), el resultado en una recopilación en paralelo no será determinista.
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
Efectos secundarios
Las operaciones que tienen efectos secundarios, como foreach
, pueden no ejecutarse como se desea en colecciones en paralelo debido a las condiciones de la carrera. Evite esto utilizando funciones que no tengan efectos secundarios, como reduce
o 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...