1

私は並列プログラミングとF#で遊んでいます。1変数関数を統合する関数を作成し、それを2つの異なる方法で並列化しようとしました。

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を使用した実装は常に線形の実装よりも遅くなります。私は何が間違っているのですか?

4

1 に答える 1

1

シーケンスは遅延ロードされることを忘れないでください。タスクが初めて開始されるのは次のとおりです。

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

そして、それらを順番に開始し、それぞれの結果をブロックします(呼び出す場合t.Result。タスクのリストを配列として保存し、結果を収集する前に呼び出し.WaitAllて、すべてが並行して開始されるようにします。

試す:

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

tasks.WaitAll()
于 2012-04-14T08:54:03.423 に答える