Работа с удивительным отсутствием ParList в scala.collections.parallel
Итак, scala 2,9 недавно появился в тестировании Debian, в результате чего с ним появились новые сфальсифицированные коллекции.
Предположим, что у меня есть некоторый код, эквивалентный
def expensiveFunction(x:Int):Int = {...}
def process(s:List[Int]):List[Int} = s.map(expensiveFunction)
теперь из маленького бита, который я почерпнул о параллельных коллекциях, прежде чем документы фактически появились на моей машине, я ожидал, что это распараллелит, просто переключив List на ParList
... но, к моему удивлению, там это не одно! (Только ParVector
, ParMap
, ParSet
...).
Как рабочий стол, этот (или однострочный эквивалент), кажется, работает достаточно хорошо:
def process(s:List[Int]):List[Int} = {
val ps=scala.collection.parallel.immutable.ParVector()++s
val pr=ps.map(expensiveFunction)
List()++pr
}
дает приблизительно улучшение производительности x3 в моем тестовом коде и обеспечивает значительно более высокий уровень использования ЦП (четырехъядерный процессор плюс гиперпоточность i7). Но это кажется неуклюжим.
Мой вопрос - это нечто вроде агрегированного:
- Почему нет
ParList
?
- Учитывая, что нет
ParList
, существует ли
Лучшая модель/идиома, которую я должен принять так, чтобы
Я не чувствую, что они пропали без вести?
- Я просто "за раз", используя списки a
в моих программах scala (например, все книги scala I
выкупили за 2,7 дня, которые меня научили) и
На самом деле я должен больше использовать
Vectors
? (Я имею в виду на земле С++
Мне обычно нужна довольно веская причина для использования
std::list
над std::vector
).
Ответы
Ответ 1
Во-первых, позвольте мне показать вам, как сделать параллельную версию этого кода:
def expensiveFunction(x:Int):Int = {...}
def process(s:List[Int]):Seq[Int] = s.par.map(expensiveFunction).seq
У вас будет Scala выяснить, что вам нужно - и, кстати, он использует ParVector. Если вы действительно хотите List
, вызовите .toList
вместо .seq
.
Что касается вопросов:
-
Нет ParList
, потому что List
- это внутренне непараллельная структура данных, потому что любая операция на ней требует обхода.
-
Вы должны кодировать символы вместо классов - Seq
, ParSeq
и GenSeq
, например. Даже рабочие характеристики List
гарантируются LinearSeq
.
-
Во всех книгах до Scala 2.8 не было библиотеки новых коллекций. В частности, коллекции действительно не разделяли последовательный и полный API. Теперь они делают, и вы выиграете, воспользовавшись им.
Кроме того, в Scala 2.7 не было такой коллекции, как Vector
- неизменяемая коллекция с индексом (почти) с индексированным индексом.
Ответ 2
List
великолепны, если вам требуется сопоставление образцов (т.е. case x :: xs
) и для эффективного добавления/итерации. Однако они не так велики, когда вам нужен быстрый доступ по индексу или разделение на куски или соединение (т.е. xs ::: ys
).
Следовательно, это не имеет большого смысла (иметь параллель List
), когда вы думаете, что такая вещь (расщепление и объединение) именно то, что необходимо для эффективного parallelism. Использование:
xs.toIndexedSeq.par
Ответ 3
A List
не может быть легко разделен на различные под-списки, что затрудняет параллелизацию. Во-первых, он имеет доступ O (n); также List
не может вырезать свой хвост, поэтому нужно включить параметр длины.
Я предполагаю, что лучшим решением будет .
Обратите внимание, что Scala s Vector
отличается от std::vector
. Последнее в основном представляет собой оболочку вокруг стандартного массива, непрерывный блок в памяти, который нужно копировать каждый раз, когда вы добавляете или удаляете данные. Scala s Vector
- это специализированная структура данных, которая позволяет эффективно копировать и разделять, сохраняя неизменные данные.