私の目的は、並列折り畳み機能を持つことです。最初は、それが達成するために かなりまっすぐ進むのようで、これは私が考えていたものです:これより一般的なparfoldr
まず コア(numCapabilities
)の数に基づいて、パーティションに入力リストを破ります。次に、各パーティションにfoldrを適用します。この場合、 は、各パーティションの折りたたまれた値のリストになります。次に、リストに foldrを再度実行して、最終値を取得します。
listChunkSize = numCapabilities
chunk n [] = []
chunk n xs = ys : chunk n zs
where (ys,zs) = splitAt n xs
parfoldr f z [] = z
parfoldr f z xs = res
where
parts = chunk listChunkSize xs
partsRs = map (foldr f z) parts `using` parList rdeepseq
res = foldr f z partsRs
明らか foldr、(a -> b -> b) -> b -> [a] -> b
、の定義は、入力リスト タイプ(よくあることができる)アキュムレータ及び結果のタイプとは異なることを意味するので、上記のコードは動作しません。
例えば、
1)foldr (+) 0 [1..10]
=>リストタイプ=アキュムレータタイプ(整数)
2)foldr (\i acc -> (i>5) && acc) True [1..10]
=>リストタイプ(整数)! は=アキュムレータ型(BOOL)
したがって、上記の私のコードを見て、地図は、第2のfoldrに引数として渡されたタイプb
のリストを生成します。しかし、2番目の foldrはタイプa
のリストを受け入れます。だから、それは動作しません。
醜い解決策は、parfoldrに異なるタイプのシグネチャを提供することです。 parfoldr :: (NFData a) => (a -> a -> a) -> a -> [a] -> a
これは動作しますが、foldrと正確には同じではありません。例 上記の1は問題ありませんが、例2はありません。 したがって、質問1は、同じタイプのシグネチャを持つようにparfoldrを定義する方法です。 foldr? 2つの折り目比較
:
input = [1..1000000]
seqfold = foldr (+) 0
parfold = parfoldr (+) 0
を私はfollを取得します。デュアルコアマシン上で時間: (NO -threadedフラグ)
$ ./test
seqfold: 4.99s
parfold: 25.16s
これらの測定から
$ ./test
seqfold: 5.32s
parfold: 25.55s
$ ./test +RTS -N1
seqfold: 5.32s
parfold: 25.53s
$ ./test +RTS -N2
seqfold: 3.48s
parfold: 3.68s
$ ./test +RTS -N3
seqfold: 3.57s
parfold: 2.36s
$ ./test +RTS -N4
seqfold: 3.03s
parfold: 1.70s
観測(-threadedオンフラグ):
foldrを与えるように思われますコアの数が増えるとランタイムが低下します。 なぜですか?
parfoldはNのためのより良いランタイムを提供します=>改善のための3
任意の提案やアイデアは大歓迎です:)
興味深い考えです。残念ながら、私が知っている限り、並行した折り目は一般化された形では存在しません... – alternative