4

質問

私は値の有限リストを持っています:

values :: [A]

...そして、それらの値に対する高価ですが純粋な関数:

expensiveFunction :: A -> Maybe B

n各値に対してその関数を並行して実行し、完了した最初の結果のみを返しJust、未完了の結果の計算を停止するにはどうすればよいですか?

takeJustsPar :: (NFData b) => Int -> (a -> Maybe b) -> [a] -> [b]
takeJustsPar maxJusts f as = ???

動機

を使用してこれを行う方法は知っていますControl.Concurrentが、Haskell の並列処理機能を使用して実験したかったのです。また、私が見つけた (わずかな) 文献は、Haskell の並列処理機能により、並列計算を生成し、多数の機能間でワークロードを適応させることが安価になることを示しているようです。

4

1 に答える 1

4

私は2つの解決策を試みました。最初のものはParモナドを使用します (つまりControl.Monad.Par):

import Control.Monad.Par (Par, NFData)
import Control.Monad.Par.Combinator (parMap)
import Data.Maybe (catMaybes)
import Data.List.Split (chunksOf)

takeJustsPar :: (NFData b) => Int -> Int -> (a -> Maybe b) -> [a] -> Par [b]
takeJustsPar n chunkSize f as = go n (chunksOf chunkSize as) where
    go _ [] = return []
    go 0 _  = return []
    go numNeeded (chunk:chunks) = do
        evaluatedChunk <- parMap f chunk
        let results      = catMaybes evaluatedChunk
            numFound     = length results
            numRemaining = numNeeded - numFound
        fmap (results ++) $ go numRemaining chunks

使用される 2 番目の試行Control.Parallel.Strategies:

import Control.Parallel.Strategies
import Data.List.Split (chunksOf)

chunkPar :: (NFData a) => Int -> Int -> [a] -> [a]
chunkPar innerSize outerSize as
  = concat ((chunksOf innerSize as) `using` (parBuffer outerSize rdeepseq))

後者は、次のように書くことができたので、はるかに構成可能になりました。

take n $ catMaybes $ chunkPar 1000 10 $ map expensiveFunction xs

take... andcatMaybes動作を並列処理戦略に組み込むのではなく。

後者のソリューションでも、ほぼ完全に利用できます。私がテストした恥ずかしい並列問題では、8 コアで 99% の使用率が得られました。Par私は同僚のコンピュータを借りていたので、モナドの利用をテストしませんでしControl.Parallel.Strategiesた.

したがって、答えは を使用することControl.Parallel.Strategiesです。これにより、はるかに構成可能な動作と優れたマルチコアの使用率が得られます。

于 2012-10-18T18:04:54.847 に答える