Работа с удивительным отсутствием 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 - это специализированная структура данных, которая позволяет эффективно копировать и разделять, сохраняя неизменные данные.