After writing this article I decided to put my money where my mouth is and started to convert a previous project of mine to use recursion-schemes
.
The data structure in question is a lazy kdtree. Please have a look at the implementations with explicit and implicit recursion.
This is mostly a straightforward conversion along the lines of:
data KDTree v a = Node a (Node v a) (Node v a) | Leaf v a
to
data KDTreeF v a f = NodeF a f f | Leaf v a
Now after benchmarking the whole shebang I find that the KDTreeF
version is about two times slower than the normal version (find the whole run here).
Is it just the additional Fix
wrapper that slows me down here? And is there anything I could do against this?
Caveats:
- At the moment this is specialized to (V3 Double).
- This is cata- after anamorphism application. Hylomorphism isn't suitable for kdtrees.
- I use
cata (fmap foo algebra)
several times. Is this good practice? - I use Edwards
recursion-schemes
package.
Edit 1:
Is this related? https://ghc.haskell.org/trac/ghc/wiki/NewtypeWrappers
Is newtype Fix f = Fix (f (Fix f))
not "free"?
Edit 2:
Just did another bunch of benchmarks. This time I tested tree construction and deconstruction. Benchmark here: https://dl.dropboxusercontent.com/u/2359191/2014-05-15-kdtree-bench-03.html
While the Core output indicates that intermediate data structures are not removed completely and it is not surprising that the linear searches dominate now, the KDTreeF
s now are slightly faster than the KDTree
s. Doesn't matter much though.
data KDTree v a = Node a (Identity (Node v a)) (Identity (Node v a)) | Leaf v a
, just to see if the extra indirection is the culprit. Make sureIdentity
is not anewtype
. – Byingtondata Identity a = MkId { unId :: a}
as you suggested. I'd say it is slower now ... but not as slow as the morphism version. – Bangweulu