Haskellでの並列「any」または「all」


9

私が何度も遭遇したパターンは、値のリストをテストすることで、値のリストをチェックし、要素の一部またはすべてが渡されたかどうかを確認する必要があるパターンです。典型的な解決策は、単に便利なビルトインを使用することであるallany

問題は、これらが連続して評価されることです。多くの場合、いずれかのスレッドがの「False」または「True」を見つけると、プロセスの完了と並行して評価する方がはるかに速くなります。プロセス間通信が必要であり、これを実装するのに十分なControl.Concurrentのどこにもまだ理解していないため、Control.Parallelを使用して短絡動作を実装できないと確信しています。allany

これは数学ではかなり一般的なパターンです(例:Miller-Rabin Primality)ので、誰かがおそらくすでにこの問題の解決策を考えているように思いますが、明らかな理由により、「並列または/および/すべて/リスト上のすべて」をグーグル検索しますhaskell」は、関連する結果を返しません。


1
Haskellの並列プログラミングと並行プログラミング、特に第2章、第3章、および第4章が役立つ場合があります。
bradrn

2
これはunambライブラリで可能です
ルキ

1
@luqui魅力的です。これをいじくります。これでall / anyの良いパラレルを書いたら、回答として投稿します。
Arcuritech

11
何かを並列化する前に、新しいプロセスをフォークするのにかかる時間内にテストできる条件の数を検討してください。
chepner

2
@chepner何言ってるの?ここではbashについて話していません!スレッドで並行処理と並列処理を行うことができます(pthreadsCの場合もHaskellのグリーンスレッドの場合も)同時Webリクエストを処理するために複数のWebサーバーを起動するのではなく、単一のプロセスで複数のスレッドを実行します。同じことが並列処理にも当てはまります。CPUの数と同じ数のスレッドを起動し、作業を均等に分割して、CPUにバインドされたタスクを処理します。このライブラリを試して、github.com
lehins /

回答:


2

多くの現実的なプログラムでは、この目的で並列戦略を使用できます。これは、不要な計算をキャンセルする明示的なメカニズムはありませんが、ガベージコレクターの実行時に暗黙的に発生するためです。具体的な例として、次のプログラムを考えます。

import Control.Concurrent
import Control.Parallel.Strategies
import Data.Int
import System.Mem

lcgs :: Int32 -> [Int32]
lcgs = iterate lcg
  where lcg x = 1664525 * x + 1013904223

hasWaldo :: Int32 -> Bool
hasWaldo x = waldo `elem` take 40000000 (lcgs x)

waldo :: Int32
waldo = 0

main :: IO ()
main = do
  print $ or (map hasWaldo [1..100] `using` parList rseq)

これは、並列リスト方式を使用して、waldo = 0それぞれ4,000万の数値の100個のPRNGストリームの出力を検索します(これは決して見つかりません)。コンパイルして実行します。

ghc -threaded -O2 ParallelAny.hs
./ParallelAny +RTS -s -N4

そしてそれは約16秒間4つのコアを固定し、最終的には印刷しFalseます。統計では、100個すべてのスパークが「変換」されるため、最後まで実行されることに注意してください。

SPARKS: 100(100 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)

次に、waldo早期に見つかる値に変更します。

waldo = 531186389   -- lcgs 5 !! 50000

mainスレッドを10秒間維持するように変更します。

main :: IO ()
main = do
  print $ or (map hasWaldo [1..100] `using` parList rseq)
  threadDelay 10000000

Trueすぐに印刷されることがわかりますが、4つのコアが100%のCPUで(少なくともしばらくの間)固定されたままであり、不要な計算が引き続き実行され、恐れられているように短絡されないことを示しています。

しかし、答えを得た後にガベージコレクションを強制すると、状況は変化します。

main :: IO ()
main = do
  print $ or (map hasWaldo [1..100] `using` parList rseq)
  performGC
  threadDelay 10000000

これで、印刷直後にCPUがアイドル状態Trueになり、ほとんどの計算が実行前にガベージコレクションされたことが統計に示されます。

SPARKS: 100(9 converted, 0 overflowed, 0 dud, 91 GC'd, 0 fizzled)

現実的なプログラムでは、performGCGCは当然のことながら定期的に実行されるため、明示は必要ありません。一部の不要な計算は、答えが見つかった後も引き続き実行されますが、多くの現実的なシナリオでは、不要な計算の割合は特に重要な要素ではありません。

特に、リストが大きく、リスト要素の個々のテストが高速である場合、並列ストラテジーは優れた実世界のパフォーマンスを持ち、バーゲンに簡単に実装できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.