2012-04-14 6 views
1

Я играю с параллельным программированием и F #. Я создал функцию, которая интегрирует 1-переменную функцию, а затем я попытался сделать это параллельно двумя различными способами:Параллельный код с Task.Factory медленнее, чем линейный

module Quadrature = 

    let Integrate (f: double -> double) (x1: double) (x2: double) (samples: int64) = 
     let step = (x2 - x1)/(double samples) 
     let samplePoints = seq {x1 + step .. step .. x2 - step} 
     let sum = samplePoints |> Seq.map (fun x -> f x) |> Seq.sum 
     let sum = sum + ((f x1) + (f x2))/2.0 
     step * sum 

    let IntegrateWithStep (f: double -> double) (x1: double) (x2: double) (step: double) = 
     let samples = (x2 - x1)/step |> round |> int64 
     Integrate f x1 x2 samples 

    let IntegrateWithTasks (f: double -> double) (x1: double) (x2: double) (samples: int64) (tasks: int) = 
     let step = (x2 - x1)/(double samples) 
     let samplesPerTask = ceil <| (double samples)/(double tasks) 
     let interval = step * samplesPerTask 
     let intervals = 
      seq { 
       for i in 0 .. (tasks - 1) do 
        let lowerBound = x1 + (double i) * interval 
        let upperBound = min (lowerBound + interval) x2 
        yield (lowerBound, upperBound) 
       } 
     let tasks = intervals 
        |> Seq.map (fun (a, b) -> Task.Factory.StartNew(fun() -> IntegrateWithStep f a b step)) 
     tasks |> Seq.map (fun t -> t.Result) |> Seq.sum 

    let IntegrateParallel (f: double -> double) (x1: double) (x2: double) (samples: int64) (tasks: int) = 
     let step = (x2 - x1)/(double samples) 
     let samplesPerTask = ceil <| (double samples)/(double tasks) 
     let interval = step * samplesPerTask 
     let intervals = 
       [| for i in 0 .. (tasks - 1) do 
        let lowerBound = x1 + (double i) * interval 
        let upperBound = min (lowerBound + interval) x2 
        yield (lowerBound, upperBound) |] 
     intervals |> Array.Parallel.map (fun (a, b) -> IntegrateWithStep f a b step) 
        |> Array.sum 

я запускаю этот код со следующим входом на машине с 4-мя ядрами:

let f = (fun x -> - 1.0 + 2.0 * x - 3.0 * x * x + 4.0 * x * x * x) 
let x1, x2 = 0.0, 1.0 
let samples = 100000000L 
let tasks = 100 

Однако метод с фабрикой задач всегда немного медленнее, чем линейный, в то время как тот, который имеет Parallel.map, дает мне хорошую скорость.

Я попытался изменить количество задач от тысяч до нескольких ядер, но реализация с Task.Factory всегда медленнее, чем линейная. Что я делаю не так?

ответ

1

Помните, что последовательности загружаются лениво. Вот первый раз задача запускается:

tasks |> Seq.map (fun t -> t.Result) |> Seq.sum 

И вы начинаете их последовательно и блокируете каждый из своего результата (при вызове t.Result Вы хотите сохранить список задач, как массив, и. затем вызвать .WaitAll перед сбором результатов, чтобы гарантировать, что все они получают начали параллельно

Try:

let tasks = intervals 
      |> Seq.map (fun (a, b) -> Task.Factory.StartNew(fun() -> IntegrateWithStep f a b step)) 
      |> Array.ofSeq 

tasks.WaitAll() 
+0

Я изменил эту строку 'задачи |> Seq.toArray |> Array.map (прикольных т ->. t.Result) |> Array.sum', и теперь он ускоряется. Спасибо! – Panos

Смежные вопросы