<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Random Hacks: Map fusion: Making Haskell 225% faster</title>
    <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Technology and Other Fun Stuff</description>
    <item>
      <title>"Map fusion: Making Haskell 225% faster" by Aaron McDaid</title>
      <description>&lt;p&gt;Jim,
&lt;span class="caps"&gt;GHC&lt;/span&gt; can&amp;#8217;t automatically know that such a rule is valid. For example, (filter f . filter g) is not equivalent to (filter (f.g))&lt;/p&gt;


	&lt;p&gt;Each author&amp;#8217;s implementation of functions called treeMap could be quite different, and it would be difficult to improve &lt;span class="caps"&gt;GHC&lt;/span&gt; such that it could correctly know which implementations could be optimized.&lt;/p&gt;</description>
      <pubDate>Sat, 30 May 2009 16:00:33 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:84d474ff-a1e0-4f66-9224-f4dbb2717f5d</guid>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance#comment-634</link>
    </item>
    <item>
      <title>"Map fusion: Making Haskell 225% faster" by Lemming</title>
      <description>&lt;p&gt;I encountered the following problem which will hurt us in case of generalized monads as sketched in http://www.randomhacks.net/articles/2007/03/15/data-set-monad-haskell-macros
and of data structure like StorableVector
http://code.haskell.org/~sjanssen/storablevector
that requires a constraint on the element types. There it makes a difference if you separately map and fold or if you do it in one go. In the first case you need a Storable constraint for intermediate result type, too. That is, these constraints make implementation issues observable via the type signature.&lt;/p&gt;</description>
      <pubDate>Fri, 04 Apr 2008 02:42:12 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:010465d6-a7fb-4a0e-9b03-da629e12468d</guid>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance#comment-567</link>
    </item>
    <item>
      <title>"Map fusion: Making Haskell 225% faster" by Sam Bronson</title>
      <description>&lt;p&gt;The &amp;#8216;x&amp;#8217; would not be calculated until it is read&amp;#8230; however, all sorts of intermediate heap objects are generated when you don&amp;#8217;t actually fuse things, which means that the GC has to be run more. Fusing maps together also gives the compiler a chance to merge the mapped functions, etc.&lt;/p&gt;


	&lt;p&gt;I wonder what the codata for a tree like this looks like&amp;#8230;&lt;/p&gt;</description>
      <pubDate>Tue, 17 Apr 2007 08:35:59 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:1e19e9eb-12b1-4cd3-bb4a-b08ef058b052</guid>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance#comment-394</link>
    </item>
    <item>
      <title>"Map fusion: Making Haskell 225% faster" by Jim</title>
      <description>&lt;p&gt;Why doesn&amp;#8217;t &lt;span class="caps"&gt;GHC&lt;/span&gt; automatically have these optimizations thanks to lazy evaluation?  It seems like the &amp;#8216;x&amp;#8217; that treeFold sees shouldn&amp;#8217;t be calculated until it is read, at which time it would simply apply the functions from treeMap.  Does &lt;span class="caps"&gt;GHC&lt;/span&gt; internally produce a million node tree with &amp;#8220;thunks&amp;#8221; first or am I misunderstanding something?&lt;/p&gt;</description>
      <pubDate>Tue, 27 Mar 2007 18:07:51 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:ed5e20ee-2def-4d91-ac6b-b7cb4e536156</guid>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance#comment-379</link>
    </item>
    <item>
      <title>"Map fusion: Making Haskell 225% faster" by Eric Kidd</title>
      <description>&lt;p&gt;An excellent &lt;a href="http://programming.reddit.com/info/13uwu/comments/c13w9e"&gt;point&lt;/a&gt; by Neel Krishnaswami:&lt;/p&gt;


&lt;blockquote&gt;

	&lt;p&gt;For [fusion] to really work, the optimization has to be part of the performance model of the language, and when it is invoked has to be predictable by the programmer. (An old, familiar example of this is mandatory tail-call optimization in functional languages, which gives you asymptotic space savings and which is very predictable in its effect.)&lt;/p&gt;


	&lt;p&gt;Enforcing purity is actually an important part of the story for fusion, because it simplifies the performance model for the programmer. Even if you could do a very fancy whole program dataflow analysis to detect side effects, a programmer couldn&amp;#8217;t easily reason about when the analysis succeeded or failed, so the optimization would be less useful.&lt;/p&gt;


&lt;/blockquote&gt;

	&lt;p&gt;Read the &lt;a href="http://programming.reddit.com/info/13uwu/comments/c13w9e"&gt;rest&lt;/a&gt; for more good insights.&lt;/p&gt;</description>
      <pubDate>Sat, 10 Feb 2007 21:05:11 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:88a9f435-550f-4651-9e5d-0eeb3f31ab50</guid>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance#comment-295</link>
    </item>
    <item>
      <title>"Map fusion: Making Haskell 225% faster" by Eric Kidd</title>
      <description>&lt;p&gt;It&amp;#8217;s true, you could achieve quite a bit with manual annotations of purity. But to get any kind of consistent optimization, you&amp;#8217;d need to use a lot of them, and do so correctly. You&amp;#8217;d also need to write your code in a largely functional style, or have some scary loop-fusion support.&lt;/p&gt;


	&lt;p&gt;An alternate approach would be to use a compiler with robust inter-procedural analysis. &lt;a href="http://llvm.org/"&gt;&lt;span class="caps"&gt;LLVM&lt;/span&gt;&lt;/a&gt;, for example, can do a lot of optimizations along these lines.&lt;/p&gt;


	&lt;p&gt;But in both cases, we&amp;#8217;re comparing my toy example, above, to a serious production compiler. The rabbit-hole goes &lt;i&gt;much&lt;/i&gt; deeper than this.&lt;/p&gt;


	&lt;p&gt;Once you start treating programs like an algebra, you can do all kinds of crazy things: automatically infer rewrite rules using Wadler&amp;#8217;s free theorems, optimize tricky concurrent code, exploit rank-2 types&amp;#8212;the list goes on and on.&lt;/p&gt;


	&lt;p&gt;Now, for many programmers, functional purity might seem a ridiculously high price to pay. But there&amp;#8217;s a surprisingly large number of functional languages out there already: query languages like &lt;span class="caps"&gt;SQL&lt;/span&gt;, OQL and &lt;span class="caps"&gt;LINQ&lt;/span&gt;; vertex and fragment shaders on your &lt;span class="caps"&gt;GPU&lt;/span&gt;; and even some hardware modeling languages (if you squint at them right).&lt;/p&gt;


	&lt;p&gt;All of these can benefit, today, from algebraic optimizers. But in each case, the price of admission is a proof of purity, whether performed by the programmer, inferred by the compiler, or enforced by the language.&lt;/p&gt;


	&lt;p&gt;I strongly suspect (but cannot prove) that supporting purity at the language level will take you much further than either of the other two approaches. And you don&amp;#8217;t have to give up state to get there; at worst, you need better support for monads.&lt;/p&gt;</description>
      <pubDate>Sat, 10 Feb 2007 20:54:48 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:2ce4d1bc-13d7-4869-b540-5fa47b961bcc</guid>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance#comment-294</link>
    </item>
    <item>
      <title>"Map fusion: Making Haskell 225% faster" by Paul Prescod</title>
      <description>&lt;p&gt;I disagree with the premise that you need the compiler to validate the side-effect-freeness of your functions. I would rather have the programmer make explicit assertions that there are no side-effects &lt;em&gt;that matter&lt;/em&gt;.&lt;/p&gt;


	&lt;p&gt;I would buy the argument that it might be valuable to have a compiler that can recognize these optimization opportunities automatically more often, but the actual assertion was that you need purity to get the optimization at all. I don&amp;#8217;t think that&amp;#8217;s been proven.&lt;/p&gt;


	&lt;p&gt;Further discussion &lt;a href="http://programming.reddit.com/info/13uwu/comments/c13w3c"&gt;here&lt;/a&gt;.&lt;/p&gt;</description>
      <pubDate>Sat, 10 Feb 2007 17:20:01 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:8d804eab-7a89-4863-9365-bc739f03c015</guid>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance#comment-293</link>
    </item>
    <item>
      <title>"Map fusion: Making Haskell 225% faster" by Matthew</title>
      <description>&lt;p&gt;This kind of thing is why I think Haskell and lazy pure-functional programming is the future (of FP, and of programming, hopefully).  Referential transparency allows this kind of logical separation of concerns and at the same time it also allows the specification of formal, provable, rules to transform programs.&lt;/p&gt;


	&lt;p&gt;The biggest benefit is that often the most naturally written code is also the best performing; while in strict languages there is usually an inversely proportional relationship between beautiful code and good performance.&lt;/p&gt;


	&lt;p&gt;The fact that &lt;span class="caps"&gt;GHC&lt;/span&gt; does so much of this work behind the scenes, and so effectively, is amazing already.  Of course, there is plenty of room for improvement.&lt;/p&gt;</description>
      <pubDate>Sat, 10 Feb 2007 12:56:20 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:f50a2cf6-5ba9-40e5-8e3f-159e53af50f3</guid>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance#comment-291</link>
    </item>
    <item>
      <title>Map fusion: Making Haskell 225% faster</title>
      <description>&lt;p&gt;&lt;b&gt;Or, how to optimize MapReduce, and when folds are faster than loops&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Purely functional programming might actually be worth the pain, if you care about large-scale optimization.&lt;/p&gt;

&lt;p&gt;Lately, I&amp;#8217;ve been studying how to speed up parallel algorithms.  Many
parallel algorithms, such as Google&amp;#8217;s &lt;a href="http://labs.google.com/papers/mapreduce.html"&gt;MapReduce&lt;/a&gt;, have two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, you transform the data by mapping one or more functions over each value.&lt;/li&gt;
&lt;li&gt;Next, you repeatedly merge the transformed data, &amp;#8220;reducing&amp;#8221; it down to a
final result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unfortunately, there&amp;#8217;s a couple of nasty performance problems lurking here.  We &lt;i&gt;really&lt;/i&gt; want to combine all those steps into a single pass, so that we can eliminate temporary working data. But we don&amp;#8217;t always want to do this optimization by hand&amp;#8212;it would be better if the compiler could do it for us.&lt;/p&gt;

&lt;p&gt;As it turns out, Haskell is an amazing testbed for this kind of
optimization. Let&amp;#8217;s build a simple model, show where it breaks, and then
crank the performance &lt;i&gt;way&lt;/i&gt; up.&lt;/p&gt;

&lt;h3&gt;Trees, and the performance problems they cause&lt;/h3&gt;

&lt;p&gt;We&amp;#8217;ll use single-threaded trees for our testbed. They&amp;#8217;re simple enough to demonstrate the basic idea, and they can be generalized to parallel systems. (If you want know how, check out the papers at the end of this article.)&lt;/p&gt;

&lt;p&gt;A tree is either empty, or it is
a node with a left child, a value and a right child:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-keyword'&gt;data&lt;/span&gt; &lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt;
            &lt;span class='hs-keyglyph'&gt;|&lt;/span&gt; &lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
  &lt;span class='hs-keyword'&gt;deriving&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Show&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;#8217;s a sample tree containing three values:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;tree&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-varid'&gt;left&lt;/span&gt; &lt;span class='hs-num'&gt;2&lt;/span&gt; &lt;span class='hs-varid'&gt;right&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
  &lt;span class='hs-keyword'&gt;where&lt;/span&gt; &lt;span class='hs-varid'&gt;left&lt;/span&gt;  &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-num'&gt;1&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
        &lt;span class='hs-varid'&gt;right&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-num'&gt;3&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use &lt;code&gt;treeMap&lt;/code&gt; to apply a function to every value in a
tree, creating a new tree:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;treeMap&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;::&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt;

&lt;span class='hs-definition'&gt;treeMap&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeMap&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
  &lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeMap&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeMap&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;treeMap&lt;/code&gt;, we can build various functions that manipulate
trees:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-comment'&gt;-- Double each value in a tree.&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeDouble&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;treeMap&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varop'&gt;*&lt;/span&gt;&lt;span class='hs-num'&gt;2&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt;

&lt;span class='hs-comment'&gt;-- Add one to each value in a tree.&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeIncr&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt;   &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;treeMap&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varop'&gt;+&lt;/span&gt;&lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What if we want to add up all the values in a tree?  Well, we could write a
simple recursive sum function:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;treeSum&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-num'&gt;0&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeSum&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
  &lt;span class='hs-varid'&gt;treeSum&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varop'&gt;+&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varop'&gt;+&lt;/span&gt; &lt;span class='hs-varid'&gt;treeSum&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But for reasons that will soon become clear, it&amp;#8217;s much better to refactor
the recursive part of &lt;code&gt;treeSum&lt;/code&gt; into a reusable
&lt;code&gt;treeFold&lt;/code&gt; function (&amp;#8220;fold&amp;#8221; is Haskell&amp;#8217;s name for &amp;#8220;reduce&amp;#8221;):&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;treeFold&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeFold&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
  &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeFold&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeFold&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;

&lt;span class='hs-definition'&gt;treeSum&lt;/span&gt; &lt;span class='hs-varid'&gt;t&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;treeFold&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;\&lt;/span&gt;&lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt;&lt;span class='hs-varop'&gt;+&lt;/span&gt;&lt;span class='hs-varid'&gt;x&lt;/span&gt;&lt;span class='hs-varop'&gt;+&lt;/span&gt;&lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-num'&gt;0&lt;/span&gt; &lt;span class='hs-varid'&gt;t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we can double all the values in a tree, add 1 to each, and sum up the
result:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;treeSum&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeIncr&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeDouble&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But there&amp;#8217;s a very serious problem with this code.  Imagine that we&amp;#8217;re
working with a million-node tree.  The two calls to &lt;code&gt;treeMap&lt;/code&gt;
(buried inside &lt;code&gt;treeIncr&lt;/code&gt; and &lt;code&gt;treeDouble&lt;/code&gt;) will each
create a &lt;i&gt;new&lt;/i&gt; million-node tree.  Obviously, this will kill our performance,
and it will make our garbage collector cry.&lt;/p&gt;

&lt;p&gt;Fortunately, we can do a lot better than this, thanks to some funky GHC
extensions.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sat, 10 Feb 2007 09:55:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:9e56e1ad-e953-4a5a-89ff-e4708979f952</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance</link>
      <category>Haskell</category>
      <category>Performance</category>
      <category>Recommended</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/290</trackback:ping>
    </item>
  </channel>
</rss>
