私は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
です。これにより、はるかに構成可能な動作と優れたマルチコアの使用率が得られます。