Использование Partitioner с PLINQ в F #

Я использую следующую функцию параллельного отображения, реализованную в PLINQ.

let parmap f (xs:list<_>) = xs.AsParallel().Select(fun x -> f x) |> Seq.toList

Я хочу улучшить свое ускорение на 4 ядрах, которые я не могу получить выше 2. Я обнаружил, что можно сделать настраиваемое разбиение на разделы, чтобы улучшить параллельную производительность. Но в основном я видел примеры C # и не знаю, как заставить его работать с F #. Следующее ничего не меняет, но я думаю, что это разделение по умолчанию, используемое TPL? Как я могу использовать здесь различные параметры (статические, динамические и т. Д.)?

let pmap_plinqlst_parts f (xs:list<_>) = 
    let parts = Partitioner.Create(xs)
    parts.AsParallel().Select(fun x -> f x) |> Seq.toList

person vis    schedule 04.06.2012    source источник
comment
Сначала вы должны выяснить, почему ограничено ускорение, и не гадать, что это исправить. В большинстве случаев разделитель по умолчанию должен быть достаточно хорош. Я не думаю, что индивидуальный вам поможет, если только у вас нет особого случая.   -  person svick    schedule 04.06.2012
comment
Что вы пытаетесь распараллелить? Универсального решения для всех проблем параллелизма не существует. В общем, Parallel.For(Each) более убедительно и легче настраивается (используя Partitioner), чем PLINQ.   -  person pad    schedule 04.06.2012
comment
Я играю с различными реализациями параллельных карт для решения проблем (например, Мандельброта), которые демонстрируют параллелизм данных в функциональном стиле. Таким образом, выполнение одной и той же работы для каждого элемента большого списка параллельно, но независимая работа может быть разного размера. Я пробовал Async Workflows, PLINQ, а также Par For. Хотя все они обеспечивают начальный параллелизм, они не масштабируются за пределы 2 ядер.   -  person vis    schedule 05.06.2012
comment
@vis: Это странно, потому что Мандельброт - это досадно параллельная проблема. Здесь важны детали реализации. Не могли бы вы опубликовать полный пример?   -  person pad    schedule 05.06.2012


Ответы (1)


Обычно пользовательский разделитель используется, если рабочие единицы очень малы. Столкнувшись с этой проблемой, вам может быть лучше переключиться на Task, а не на Async, поскольку он обычно больше подходит для меньших, но более многочисленных объемов работы, а Async больше подходит для операций типа IO, где задержка обычно больше.

Например, вы можете последовательно распределить вычисления между параллельными потоками. Доходность будет варьироваться в зависимости от размера рабочих единиц, а также от общего количества элементов.

Ни в одном из упомянутых вами методов нет ограничений на масштабирование. Я распараллелил вычисление Блэка-Шоулза и смог получить около 6,8x на 8-ядерной машине с использованием Async.Parallel. Хотя это и не был идеальный механизм, я использовал простое разделение работы между начальными последовательностями, которые были переданы в Async.Parallel.

Вы уверены, что у вас настоящий четырехъядерный компьютер или двухъядерный с гиперпоточностью?

person 7sharp9    schedule 10.10.2012