2011-01-04 9 views
6

純粋な計算に接続されたHaskellの並列ストラテジーの使用法と説明がよく見られます(たとえば、fib)。しかし、私はモナド構造で使用されることはよくありません:ST sまたはIOに適用した場合、parの効果と関連する関数の合理的な解釈がありますか?そのような使用からスピードアップが得られるでしょうか?モナドでの並列ストラテジーの使用

+0

IO操作を特定の順序で実行する(たとえば、ファイルを開くよりも、ファイルを開くことよりも閉じるなど)。そこでは何を並列化したいですか? – helium

+1

@helium:これは主に、可変データまたはFFIを使用していると思われます。 –

+0

私はこれを疑問に思っています。私はしばしばいくつかの大きなファイル(100メガ)を開いて、それらを並列スレッドで解凍し、開いた後にそれらを使って作業します。彼らは素晴らしいパフォーマンスの向上を見るのに十分な大きさですが、私はそれらをメモリに保持するのに十分なほど小さいです。私はハスケルでこれをどうやってするのか疑問に思っています。 –

答えて

12

IOモナドの並列性は、より正確には「並行性」と呼ばれ、モジュールのforkIOとフレンドによってサポートされています。

STモナドを並列化することの難しさは、STが必然的にシングルスレッドであることです。これがその目的です。原則として並列評価をサポートすることができるSTモナドControl.Monad.ST.Lazyの怠惰な変形がありますが、私はこれをやろうとしている人は知らないのです。

Evalと呼ばれる並列評価用の新しいモナドがあります。このモナドはparallel packageの最新バージョンにあります。最近はparpseqの代わりにEvalのモナドをrparrseqで使用することをお勧めします。なぜなら、より堅牢で読みやすいコードにつながるからです。例えば、通常のfib例は

fib n = if n < 2 then 1 else 
     runEval $ do 
      x <- rpar (fib (n-1)) 
      y <- rseq (fib (n-2)) 
      return (x+y) 
1

を書き込むことができ、これは理にかなっているが、一般的に、あなたがそれを行うべきではない、いくつかの状況があります。以下の調べ:

doPar
doPar = 
    let a = unsafePerformIO $ someIOCalc 1 
     b = unsafePerformIO $ someIOCalc 2 
    in a `par` b `pseq` a+b 

aための計算を巻き起こしている、メインスレッドはbを評価します。しかし、メインスレッドがbの計算を終了した後、aも評価される可能性があります。今度は、aを評価する2つのスレッドがあります。つまり、IOアクションの一部が2回(またはそれ以上)実行されることを意味します。しかし、一方のスレッドがaの評価を終了すると、もう一方のスレッドはこれまでの処理を破棄します。これを安全にするには、実際にはいくつかのことが必要です。

  1. IOアクションを複数回実行することは安全です。
  2. IOアクションの一部のみを実行することは安全です(クリーンアップがないなど)
  3. IOアクションには競合条件がありません。あるスレッドがaを評価する際に何らかのデータを変更すると、他のスレッドもaで動作しますか?おそらくそうではありません。
  4. どれ外国呼び出しがされている再入あなたsomeIOCalcこの

    someIOCalc n = do 
        prelaunchMissiles 
        threadDelay n 
        launchMissiles 
    

    のように見える場合、それはparとでこれを使用するために絶対に安全ではありません

(あなたはもちろん、一般的に同時実行のためにこれを必要とします) unsafePerformIO

今、それはこれまでに価値がありますか?多分。スパークは安く、スレッドよりも安いので、理論的にはパフォーマンスが向上するはずです。実際には、それほど多くはないでしょう。ローマLeschinskyは素敵なblog post about thisを持っています。

私は個人的には、約forkIOの理由を考えるのがずっと簡単です。

関連する問題