スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#による並行プログラミング入門(7) Parallel Loops(4) スレッド毎の変数

 まずは次の関数をみてください。 
 
> let example1() = 
    let sumData1 = ref 0L 
    let lockObjForSumData1 = new obj() 
    Parallel.For(1L,100000001L,(fun i  ->lock(lockObjForSumData1) 
                                          (fun () -> sumData1:= !sumData1 + i))) 
    |> ignore 
    !sumData1;; 
 
val example1 : unit -> int64 
 
これは、1,100000001までの和を求めて返す関数です。lockを使った排他処理を用いて頻繁にsumData1にアクセスしているので、処理速度はかなり落ちています。 
ちなみに測ってみると 
 
> #time;; 
--> 現在タイミングはオンです 
 
> example1();; 
リアル: 00:00:08.604、CPU: 00:00:33.468、GC gen0: 0, gen1: 0, gen2: 0 
val it : int64 = 5000000050000000L 
 
そこで、今回はスレッド毎の変数を利用してこれを少し高速化してみます。 
スレッド毎の変数を利用するには、次の形のParallel.Forを利用します。 
 
static member For :  
        fromInclusive:int64 *  
        toExclusive:int64 *  
        localInit:Func<'TLocal> *  
        body:Func<int64, ParallelLoopState, 'TLocal, 'TLocal> *  
        localFinally:Action<'TLocal> -> ParallelLoopResult  
 
(int64をint32に置き換えた形もあります。) 
 
この形のParallel.Forを呼び出すと、スレッド毎に'TLocal型の変数の場所を準備してくれます。 
 
引数のタプルの第3成分のlocalInit:Func<'TLocal>は最初に呼び出され、この返り値で、この変数が初期化されます。 
 
第4成分のbody:Func<int64, ParallelLoopState, 'TLocal, 'TLocal>で、毎回の処理に対して呼び出されます。「引数のint64の場所にイテレーション値(ループ変数)、ParallelLoopStateに暗黙的に作成されたParallelLoopStateオブジェクト、'TLocalにスレッド毎の変数」が渡されて計算され、スレッド毎の変数がその計算結果に変更されます。 
 
第5成分のlocalFinally:Action<'TLocal>は、スレッドに仕事がなくなると、スレッド毎の変数の最終値を引数として、呼び出されます。 
 
ではこれを使って関数example1を書き直します。 
 
> let example2() = 
    let sumData1 = ref 0L 
    let lockObjForSumData1 = new obj() 
    Parallel.For(1L, 
                 100000001L, 
                 Func<int64>(fun () -> 0L ), 
                 (fun i loopState localSum -> localSum + i), 
                 (fun localSum ->lock(lockObjForSumData1) 
                                    (fun () ->sumData1:= !sumData1 + localSum ))) 
    |> ignore 
    !sumData1;; 
 
val example2 : unit -> int64 
 
タプルの第3成分がFunc<int64>(fun () -> 0L )となっていますが、ここをちゃんとデリゲートFunc<int64>型にキャストしないと、型推論上エラーが出るので、苦肉の策です。 
 
実行してみます。 
 
> example2 ();; 
リアル: 00:00:00.514、CPU: 00:00:01.906、GC gen0: 0, gen1: 0, gen2: 0 
val it : int64 = 5000000050000000L 
 
example1の方が 
リアル: 00:00:08.604、CPU: 00:00:33.468、GC gen0: 0, gen1: 0, gen2: 0 
でしたので、かなり高速化しています。 
 
では総スレッド数がわかるようにコードを少し変更してみます。 
変更点は 
printfn "排他処理でスレッド部分和 %A を書き込みます" localSum 
という一文を追加するだけです。 
 
> let example3() = 
    let sumData1 = ref 0L 
    let lockObjForSumData1 = new obj() 
    Parallel.For(1L, 
                 100000001L, 
                 Func<int64>(fun () -> 0L ), 
                 (fun i loopState localSum -> localSum + i), 
                 (fun localSum ->lock(lockObjForSumData1) 
                                    (fun () ->printfn "排他処理でスレッド部分和 %A を書き込みます" localSum 
                                              sumData1:= !sumData1 + localSum ))) 
    |> ignore 
    !sumData1 
;; 
 
val example3 : unit -> int64 
 
> example3 ();; 
排他処理でスレッド部分和 498409429409520L を書き込みます 
排他処理でスレッド部分和 385055441925336L を書き込みます 
排他処理でスレッド部分和 42708167048697L を書き込みます 
排他処理でスレッド部分和 804624348454776L を書き込みます 
排他処理でスレッド部分和 931199740355416L を書き込みます 
排他処理でスレッド部分和 380574767129240L を書き込みます 
排他処理でスレッド部分和 49912633790416L を書き込みます 
排他処理でスレッド部分和 793902682033392L を書き込みます 
排他処理でスレッド部分和 549002014181280L を書き込みます 
排他処理でスレッド部分和 40817003628288L を書き込みます 
排他処理でスレッド部分和 121352584224992L を書き込みます 
排他処理でスレッド部分和 1255823052658L を書き込みます 
排他処理でスレッド部分和 44877292288128L を書き込みます 
排他処理でスレッド部分和 100639046081096L を書き込みます 
排他処理でスレッド部分和 152290719916277L を書き込みます 
排他処理でスレッド部分和 103378356480488L を書き込みます 
 
val it : int64 = 5000000050000000L 
ということで、のべ16のスレッドを使用していたようです。 
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。