2012-01-13 11 views
8

私はパラレル化したい機能frequencyByを持っています。ここでは、簡単なテストケースを、次のとおりです。HaskellでのParallel Strategiesの使用方法

import Control.Parallel.Strategies 
import Control.DeepSeq 
import System.Environment 

frequencyBy :: (a -> b -> Bool) -> [a] -> [b] -> [(a,Int)] 
frequencyBy f as bs = map 
    (\a ->(a, foldr (\b -> if f a b then (+) 1 else id) 0 bs)) as 

main :: IO() 
main = do 
    x:xs <- getArgs 
    let result = frequencyBy (==) [1::Int .. 10000] [1 .. (read x)] `using` 
       parList rdeepseq 
    print $ product $ map snd $ result 

私は並行してfrequencyBymapを実行したいと思います。私はparList rdeepseq(すべてmainのすべての他のものを使用してこれを達成しようとしているすべてが離れて最適化されていないことを確認することです)。しかし、これはうまくいきません.2つのスレッドは、同じ時間に1つのスレッドの2倍の作業を行います。私はここで間違っていることを理解していない。

+3

2つのスレッドが同じ時間に1つのスレッドの2倍の作業をする場合、正しく並列化されているわけではありませんか? – ehird

答えて

9

オーバーヘッドがどれほど大きくなっているかに応じて、処理が遅くなる可能性があります。xです。各スパークでやっている作業が各スパークを発生させるのにかかる時間(とスケジューリングオーバーヘッドなど)に匹敵する場合は、問題が発生します。

parListChunkparListChunk 64 rdeepseq;どのチャンク・サイズを使用するかを実験する必要があります。あなたの現在の戦略は、リストのすべての要素のスパークを作成している間、parListChunkは、リスト内の特定のサイズの各チャンクに対してスパークを作成し、そのチャンクの各要素に対して順番に指定する戦略を使用します。

ちなみにfoldrfrequencyByはおそらく過度のサンクの作成のために物事を遅くするでしょう。何かのように

frequencyBy :: (a -> b -> Bool) -> [a] -> [b] -> [(a,Int)] 
frequencyBy f as bs = map (\a -> (a, sum . map (const 1) . filter (f a) $ bs)) as 

を修正する必要があります。

もちろん、いつものように、-O2でコンパイルし、+RTS -Nで実行していることを確認してください。

+0

これは同じコードではありません。 OPの関数は 'sumと等価です。どちらも私にとっては改善されていませんが( 'length'を使うのはずっと遅いですが)map(const 1)$ filter(f a)bs'や' length $ filter(f a)bs'です。 –

+0

'parListChunk 2 rdeepseq'はすでにトリックを行い、(1つのスレッドと比較して)2つのスレッドで半分の時間しかかからないようにします。これは奇妙に思えますが、1のチャンクを評価するとオーバーヘッドが大きくなり、2のチャンクは完璧な並列化につながります。 – user362382

+0

私は 'sumを使用しました。 map(const 1)$ filter(f a)bs'を前に実行していましたが、手動で 'foldr'に融合させるのが速かったです。 – user362382

7

あなたの並列性はあまりに細かすぎると思います。 parListはすべての要素を並行して評価しようとしますが、いずれの要素に対してもそれほど多くの作業はありません。

parListからparListChunk 500に変更すると、実行時間がほぼ50%増加します。私はデュアルコアマシンを利用しています。

参考として、私はx=20000でテストしました。

関連する問題