Каков наилучший способ создания карты параллельно с Scala? - PullRequest
3 голосов
/ 30 сентября 2011

Предположим, у меня есть коллекция, которую нужно преобразовать в карту, но не в формате один в один, как в методе карты.

var map = collection.mutable.HashMap()
for (p <- dataList.par) {
  if(cond1(p)) {
    map += (p, true)
  } else {
    // do nothing
  }
}

Я придумала несколько решений и хочу узнать, что лучше.

  1. map.synchronize { map += (p, true) }

  2. используйте актера для обновления карты.Но я не знаю, как дождаться, пока все актеры не выполнят задачу

  3. yield Some(p) or None, а затем запустить foreach { case Some(p) => map += (p, true)}.Но я не знаю, как сделать это последовательным, если первый итератор из параллельных коллекций.

Ответы [ 2 ]

5 голосов
/ 30 сентября 2011

breakOut, упомянутый в другом ответе, разрешается на фабрику строителя для сбора ожидаемого типа map. Ожидаемый тип map - mutable.Map[Int, Boolean].

Поскольку фабрика компоновщика обеспечивается последовательным сбором, collect не будет работать параллельно:

scala> val cond1: Int => Boolean = _ % 2 == 0
cond1: Int => Boolean = <function1>

scala> val dataList = 1 to 10
dataList: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> val map: mutable.Map[Int,Boolean] = dataList.par.collect{case p if cond1(p) => println(Thread.currentThread); (p, true)}(breakOut)
Thread[Thread-8,5,main]
Thread[Thread-8,5,main]
Thread[Thread-8,5,main]
Thread[Thread-8,5,main]
Thread[Thread-8,5,main]
map: scala.collection.mutable.Map[Int,Boolean] = Map(10 -> true, 8 -> true, 4 -> true, 6 -> true, 2 -> true)

Вы можете видеть это по имени потока - поток должен содержать имя ForkJoin -что-то.

Правильный путь

Правильный способ сделать это должен сначала использовать breakOut с ожидаемым типом, являющимся параллельной картой, чтобы collect действовал параллельно:

scala> val map: parallel.mutable.ParMap[Int,Boolean] = dataList.par.collect{case p if cond1(p) => println(Thread.currentThread);(p, true)}(breakOut)
Thread[Thread-9,5,main]
Thread[Thread-9,5,main]
Thread[Thread-9,5,main]
Thread[Thread-9,5,main]
Thread[Thread-9,5,main]
map: scala.collection.parallel.mutable.ParMap[Int,Boolean] = ParHashMap(10 -> true, 8 -> true, 4 -> true, 6 -> true, 2 -> true)

и затем вызовите seq по результату collect, так как seq всегда O(1).

ОБНОВЛЕНИЕ : только что проверил - похоже, работает корректно с транком, но не с 2.9.1. Финал.

Патч

Но, как вы можете видеть, это тоже не работает, потому что это ошибка, и она будет исправлена ​​в следующей версии Scala. Обходной путь:

scala> val map: parallel.mutable.ParMap[Int, Boolean] = dataList.par.collect{case p if cond1(p) => println(Thread.currentThread);(p, true)}.map(x => x)(breakOut)
Thread[ForkJoinPool-1-worker-7,5,main]
Thread[ForkJoinPool-1-worker-3,5,main]
Thread[ForkJoinPool-1-worker-0,5,main]
Thread[ForkJoinPool-1-worker-8,5,main]
Thread[ForkJoinPool-1-worker-1,5,main]
map: scala.collection.parallel.mutable.ParMap[Int,Boolean] = ParHashMap(10 -> true, 8 -> true, 4 -> true, 6 -> true, 2 -> true)

scala> val sqmap = map.seq
sqmap: scala.collection.mutable.Map[Int,Boolean] = Map(10 -> true, 8 -> true, 4 -> true, 6 -> true, 2 -> true)

С примечанием, что окончательный map будет в настоящее время выполняться последовательно.

В качестве альтернативы, если с вами достаточно parallel.ParMap, вы можете сделать:

scala> val map: Map[Int, Boolean] = dataList.par.collect{case p if cond1(p) => println(Thread.currentThread);(p, true)}.toMap.seq
Thread[ForkJoinPool-1-worker-2,5,main]
Thread[ForkJoinPool-1-worker-3,5,main]
Thread[ForkJoinPool-1-worker-7,5,main]
Thread[ForkJoinPool-1-worker-1,5,main]
Thread[ForkJoinPool-1-worker-8,5,main]
map: scala.collection.Map[Int,Boolean] = Map(10 -> true, 6 -> true, 2 -> true, 8 -> true, 4 -> true)
5 голосов
/ 30 сентября 2011

Не уверен, что на самом деле будет работать лучше, но это должно сделать оценку условий параллельной:

import scala.collection._
val map: mutable.Map[Int, Boolean] 
  = dataList.par.collect{case p if cond1(p) => (p, true)}(breakOut)

(с изменяемой картой, как это сделал ваш код, но это не обязательно).

Контекст должен давать тип ожидаемого результата (отсюда : mutable.Map[Int, Boolean]), чтобы breakOut работал.

Редактировать: breakOut - это scala.collection.breakOut. Операция коллекций, возвращающая коллекцию (здесь collect), принимает неявный аргумент bf: CanBuildFrom[SourceCollectionType, ElementType, ResultType]. Неявные CanBuildFroms, предоставляемые библиотекой, упорядочены таким образом, чтобы возвращался наилучший возможный ResultType, а наилучший означает ближайший к типу исходной коллекции. breakOut передается вместо этого неявного аргумента, так что можно выбрать другой CanBuildFrom, следовательно, тип результата. breakOut выбирает CanBuildFrom независимо от типа коллекции источника. Но тогда есть много доступных последствий и нет правила приоритета. Вот почему тип результата должен быть задан контекстом, чтобы можно было выбрать одно из последствий.

Подводя итог, если вместо неявного аргумента передается breakOut, результат будет построен в соответствии с типом, ожидаемым в контексте.

...