スポンサーサイト

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

F#による並行プログラミング入門(9) Parallel ForEach

 Parallel.Forは連続した整数と、それに対応する仕事を並行処理するメソッドでしたが、Parallel.ForEachはIEnumerable型の値と、それに対応する仕事を並行処理するメソッドです。 
 
例えば 
Parallel.For(1,101,(fun i -> doWork i)) 
はParallel.ForEachを用いると 
Parallel.ForEach([1 .. 100],(fun i -> doWork i)) 
と書くことができます。もちろん[1 .. 100]の部分は[| 1 .. 100 |] でも、seq{1 .. 100}でも結果は同じです。 
 
文字列のリストに対して、リスト内の文字の文字数の総和をParallel.ForEachを使ってやってみると次のようになります。 
 
> let sumStrLength = ref 0 
Parallel.ForEach ( ["abc";"ee";"fefff"],(fun (s:string) -> Interlocked.Add(sumStrLength,s.Length)|>ignore));; 
 
val sumStrLength : int ref = {contents = 10;} 
 
(注意)この場合、関数の引数部分は型注釈が必要です。 
        関数部分はちゃんとunit型を返すように|>ignoreをつけておく必要があります。 
         
さてParallel.ForEachも多数のオーバーロードがあるのですが、これはほとんどがParallel.Forの 
引数のタプル内のfromInclusive:int * toExclusive:intの部分をIEnumerable<'TSource>に置き換えた形になっています。 
ということでここでは、Parallel.Forには対応する形のないParallel.ForEach独自のメソッドのみを紹介しておきます。 
 
ここで取り上げるのは 
static member ForEach :  
        source:OrderablePartitioner<'TSource> *  
        body:Action<'TSource, ParallelLoopState, int64> -> ParallelLoopResult  
の形です。 
とりあえず、ひとつ例をあげてみます。 
 
> open System 
open System.Threading 
open System.Threading.Tasks 
open System.Collections.Concurrent //#これが新たに必要 
;; 
 
> let sum = ref 0 
Parallel.ForEach(Partitioner.Create(0,1001),//#1 
                (fun range  -> printfn "%A" range  
                               let partSum = Seq.sum (seq{(fst range) .. ((snd range)-1)}) 
                               Interlocked.Add(sum,partSum)  
                               |>ignore));; 
(0, 83) 
(166, 249) 
(249, 332) 
(41(498, 581) 
5, 498) 
(581, 664) 
(664, 747) 
(747, 830) 
(830, 913) 
(913, 996) 
(83, 166) 
(996, 1001) 
(332, 415) 
 
val sum : int ref = {contents = 500500;} 
 
#1の部分でSystem.Collections.Concurrent.PartitionerクラスのCreateメソッドを用いて0から1001までを連続するいくつかの部分に分けます。(分割数はコア数などによって自動的に調整されます。) 
するとParallel.ForEachにより、「それぞれの部分の最初の数と、最後の数+1がint*int型のタプル」として引数の第二成分である関数に渡されます。上の例ではそれを表示してから部分和をとり、最後に共通の値sumに加算しています。 
 
なお次のようにすると分割した部分に含まれる数を指定することができます。 
 
> let sum = ref 0 
Parallel.ForEach(Partitioner.Create(0,1001,300),//#####ここが変わった##### 
                (fun range  -> printfn "%A" range  
                               let partSum = Seq.sum (seq{(fst range) .. ((snd range)-1)}) 
                               Interlocked.Add(sum,partSum)  
                               |>ignore));; 
(0, 300) 
(300, 600) 
(600, 900) 
(900, 1001) 
 
val sum : int ref = {contents = 500500;} 
 
なおパーティション分割についてはいろいろな種類があるので詳しくはhttp://msdn.microsoft.com/ja-jp/library/dd997411.aspxを参照してください。 
スポンサーサイト

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

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

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

この人とブロともになる

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