Ускоряет ли F # Async.Parallel вычисления? - PullRequest
8 голосов
/ 31 января 2011

Действительно ли конструкция Async.Parallel помогает ускорить вычисления в многоядерной системе? Как-то здесь задействованы «Задачи» .NET TPL?

open System;

let key = Console.ReadKey(true);
let start = System.DateTime.Now

let pmap f l = seq { for a in l do yield async {return f a} } |> Async.Parallel |> Async.RunSynchronously
let map f l = seq {for a in l do yield f a}

let work f l = 
 match key.KeyChar with 
  | '1' -> pmap f l  
  | '2' -> Seq.toArray (map f l) 
  | _ -> [||]

let result = work (fun x -> (x * x) / 75) (seq { 1 .. 100000*3})
let endtime = DateTime.Now - start 

printfn "%A"endtime;
let pause = Console.ReadKey(true);

Полагаю, некоторые из вас объяснят это теоретически, но я также был бы признателен за некоторые испытания в реальном мире.

Ответы [ 3 ]

12 голосов
/ 31 января 2011

Использование F # async для чисто связанных с ЦП задач работает только в том случае, если задачи выполняют более сложную операцию.Если вы пытаетесь распараллелить код, который делает что-то очень простое, то лучше использовать PLINQ (и библиотеку параллельных задач), которая более оптимизирована для подобных задач.

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

// Turn on timing in F# interactive
#time 
let data = [| 1 .. 5000000*3 |]

// Use standard 'map' function for arrays
let result = Array.map (fun x -> (x * x) / 75) data 
// Use optimized parallel version
let result = Array.Parallel.map (fun x -> (x * x) / 75) data

Обратите внимание, что использование Array.map само по себе намного быстрее, чем использование выражений последовательности, а затем преобразование результата в массив.Если вы хотите использовать более сложные операции, чем сопоставление, то F # PowerPack содержит модуль PSeq с функциями, аналогичными тем, которые есть в Seq или List:

#r @"FSharp.PowerPack.Parallel.Seq.dll"

data 
|> PSeq.map (fun a -> ...)
|> PSeq.filter (fun a -> ...)
|> PSeq.sort
|> Array.ofSeq

Если вы хотите узнать больше об этомЯ недавно написал серию блогов о параллельном программировании на F # .

9 голосов
/ 31 января 2011

То, что делает pmap, - это создание списка из 300 000 объектов задач, организация их параллельного запуска и только затем фактический запуск их параллельно.Другими словами, один поток будет сидеть там, создавая 300 000 объектов и помещая их в очередь в пуле потоков.Только тогда они будут выполнены.

Поскольку ваша задача настолько тривиальна (умножение и деление), затраты на создание задачи, ее планирование и обработку ее результата на намного большепросто запускаю вычисления.Это означает, что метафора async не подходит для этой операции.Для этого лучше использовать PLINQ.

3 голосов
/ 31 января 2011

При таком простом вычислении лучше всего создать только несколько асинхронных потоков (вероятно, по одному для каждого процессора), а затем каждый вычислить часть вашего ответа. Как ответил Гейб, вы тратите все свое время на создание объектов задач.

Используя этот тип плана, я получаю ускорения, которые масштабируются очень близко к числу процессоров (самое большее, что я пробовал, это 8 ... Я понимаю, что это не будет масштабироваться вечно)

Я полагаю, что написание утилиты для этого - больше, чем просто вызов PLINQ, но если у вас есть утилита типа pmap, вы можете легко ее использовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...