> foldMap :: Monoid m => (a -> m) -> [a] -> m
> foldMap f [] = mempty
> foldMap f [x] = f x
> foldMap f xs =
> let (ys,zs) = splitAt (length xs `div` 2) xs
> ys' = foldMap f ys
> zs' = foldMap f zs
> in zs' `par` (ys' `pseq` (ys' `mappend` zs'))
> ~~~
>
> How can this pattern be implemented in Haskell efficiently?
0. use actual lists: does not really work: length and splitAt are expensive.
(Note: it does work, sort of, in cases like mergesort,
when you foldMap the "sort" part only. It seems the overhead
is small compared to the work that "merge" does.)
> 1. Data.Sequence (from containers)
> As finger trees are already balanced they look like a good candidate
> for parallel consumption. However, the current implementation of the
> `Foldable` instance is sequential.
Indeed. But you have Data.Sequence.splitAt (in log time),
so you can take the above code. When I tried this, I found the problem
is elsewhere: You generally want to avoid splitting and sparking (par)
deep down the tree (once you exhausted the actual number
of capabilities (= cores)). Then you want to use a sequential fold -
and I found Data.Sequence.foldl (rather, the Foldable instance)
quite expensive. You could instead use toList, but that's expensive as
well (should it be?)
So what I'm using now is Data.Vector(.Unboxed), and this looks good.
But cf. http://article.gmane.org/gmane.comp.lang.haskell.cafe/90211
foldMap :: Monoid m => (a -> m) -> [a] -> mfoldMap f [] = memptyfoldMap f [x] = f xfoldMap f xs =let (ys,zs) = splitAt (length xs `div` 2) xsys' = foldMap f yszs' = foldMap f zsin zs' `par` (ys' `pseq` (ys' `mappend` zs'))~~~How can this pattern be implemented in Haskell efficiently?
foldMap :: Monoid m => (a -> m) -> [a] -> mfoldMap f [] = memptyfoldMap f [x] = f xfoldMap f xs =let (ys,zs) = splitAt (length xs `div` 2) xsys' = foldMap f yszs' = foldMap f zs
> a) only spark of computations when the granularity is high enough to make it
worth while
> b) only spark of computations that are evaluated to normal forms.
sure, that's what I do:
http://www.imn.htwk-leipzig.de/~waldmann/ss11/skpp/code/kw24/mps-vector.hs
I tried this with Data.List, and Data.Sequence,
but Data.Vector.Unboxed really makes it fly.
I found this post very helpful:
http://article.gmane.org/gmane.comp.lang.haskell.cafe/90211
thanks! The version with unboxed vectors looks like a good baseline to
benchmarks other solutions.
>> a) only spark of computations when the granularity is high enough to make it
> worth while
>> b) only spark of computations that are evaluated to normal forms.
>
> sure, that's what I do:
Not exactly. Your code sparks of computations as long as there are
capabilities. You will use two sparks for a vector of length two.
The threshold approach can create more sparks than there are
capabilities which is good if the amount of work for each spark
fluctuates and unnecessary if all sparks require the same amount of
work. It creates less sparks for small inputs but that may be
negligible as small inputs can be processed fast anyway..
Sebastian