多くの現実的なプログラムでは、この目的で並列戦略を使用できます。これは、不要な計算をキャンセルする明示的なメカニズムはありませんが、ガベージコレクターの実行時に暗黙的に発生するためです。具体的な例として、次のプログラムを考えます。
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)
現実的なプログラムでは、performGC
GCは当然のことながら定期的に実行されるため、明示は必要ありません。一部の不要な計算は、答えが見つかった後も引き続き実行されますが、多くの現実的なシナリオでは、不要な計算の割合は特に重要な要素ではありません。
特に、リストが大きく、リスト要素の個々のテストが高速である場合、並列ストラテジーは優れた実世界のパフォーマンスを持ち、バーゲンに簡単に実装できます。