<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
		>
<channel>
	<title>Comments on: Higher-Order Fork/Join Operators</title>
	<atom:link href="http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/feed" rel="self" type="application/rss+xml" />
	<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators</link>
	<description>(permanently in beta)</description>
	<lastBuildDate>Sun, 29 Aug 2010 20:01:44 -0700</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<item>
		<title>By: Daniel K</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4944</link>
		<dc:creator>Daniel K</dc:creator>
		<pubDate>Sat, 27 Feb 2010 00:43:25 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4944</guid>
		<description>Then again the Integer result of the function will overflow before n is large enough to be worth parallelizing, so it should probable use something like BigInteger instead :)</description>
		<content:encoded><![CDATA[<p>Then again the Integer result of the function will overflow before n is large enough to be worth parallelizing, so it should probable use something like BigInteger instead <img src='http://www.codecommit.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Daniel K</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4943</link>
		<dc:creator>Daniel K</dc:creator>
		<pubDate>Sat, 27 Feb 2010 00:37:53 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4943</guid>
		<description>Ok, I realize this is an old post, but it&#039;s still #8 when I google for fork join :)

How about expressing factorial like this?:

def fac(n: Int): Int = if (n == 0) 1 else mul (1, n)

def mul (from: Int, to: Int): Int = {
  if (from == to)
    return from
  if (to - from == 1)
    return to * from
  val mid = (from + to) / 2
  mul (from, mid) * mul (mid + 1, to)
}

Now fac(n) is recursively broken into independent subtasks, and should be an ideal candidate for fork join. For optimal performance it should fall back to sequential computation earlier than (to - from == 1). 

NB: The mul function obviously makes some assumptions about the values of its parameters, but that&#039;s ok if it is only used to help implement factorial. I haven&#039;t tested it, so I can&#039;t guarantee it compiles, let alone computes the correct result. But I think the general idea is clear at least :)</description>
		<content:encoded><![CDATA[<p>Ok, I realize this is an old post, but it&#8217;s still #8 when I google for fork join <img src='http://www.codecommit.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>How about expressing factorial like this?:</p>
<p>def fac(n: Int): Int = if (n == 0) 1 else mul (1, n)</p>
<p>def mul (from: Int, to: Int): Int = {<br />
  if (from == to)<br />
    return from<br />
  if (to &#8211; from == 1)<br />
    return to * from<br />
  val mid = (from + to) / 2<br />
  mul (from, mid) * mul (mid + 1, to)<br />
}</p>
<p>Now fac(n) is recursively broken into independent subtasks, and should be an ideal candidate for fork join. For optimal performance it should fall back to sequential computation earlier than (to &#8211; from == 1). </p>
<p>NB: The mul function obviously makes some assumptions about the values of its parameters, but that&#8217;s ok if it is only used to help implement factorial. I haven&#8217;t tested it, so I can&#8217;t guarantee it compiles, let alone computes the correct result. But I think the general idea is clear at least <img src='http://www.codecommit.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Ismael Juma</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4107</link>
		<dc:creator>Ismael Juma</dc:creator>
		<pubDate>Tue, 07 Oct 2008 20:50:26 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4107</guid>
		<description>I am not sure if one can really say that fork-join &quot;has generally remained shrouded in a murky cloud of academic trappings and formalisms&quot;, but if so it will surely change once JDK7 is released since it should include Doug Lea&#039;s fork-join library. The source code is in CVS[1] and from my experiments it works great. There is also a version that uses BGGA closures[2].

Ismael

[1] gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/forkjoin/
[2] www.javac.info/jsr166z/jsr166z/forkjoin/package-summary.html</description>
		<content:encoded><![CDATA[<p>I am not sure if one can really say that fork-join &#8220;has generally remained shrouded in a murky cloud of academic trappings and formalisms&#8221;, but if so it will surely change once JDK7 is released since it should include Doug Lea&#8217;s fork-join library. The source code is in CVS[1] and from my experiments it works great. There is also a version that uses BGGA closures[2].</p>
<p>Ismael</p>
<p>[1] gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/forkjoin/<br />
[2] <a href="http://www.javac.info/jsr166z/jsr166z/forkjoin/package-summary.html" rel="nofollow">http://www.javac.info/jsr166z/jsr166z/forkjoin/package-summary.html</a></p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Daniel Spiewak</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4069</link>
		<dc:creator>Daniel Spiewak</dc:creator>
		<pubDate>Sun, 28 Sep 2008 19:03:54 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4069</guid>
		<description>@Johannes

All true (well, see next paragraph).  The trouble is that algorithms which are intuitively parallelizable do not grow on trees.  Worse, they tend to be a little more complicated than a simple factorial and fibonacci.  I could have used them, but to do so would have detracted from the main point and made the final result a little less convincing.  14.6 vs 7.5 seconds is a pretty impressive comparison, and while the ratio would have been the same with a O(n) fibonacci, the difference would not have been so dramatic.

I actually disagree with you that the naive implementation of Fibonacci is not parallelizable.  The fact that each calculation depends on two previous ones does not necessarily obviate the possibility, as shown by the fact that I did it.  :-)  Doug Lea himself in explaining fork/join used pseudo-code algorithms which are assumed to be recursive in this fashion, despite the fact that they would share the same &quot;unparallelizable&quot; characteristics.

I think what you are seeing in Fibonacci is the issue of blocking recursion, each computation requiring its precedents and blocking until they are available.  This is the problem which is solved by using actors in a linear chain.  Each computation completes and then passes the result *specifically* to another Promise.  Essentially, the actor strategy used transforms the Fibonacci computation from top-down to bottom-up.  It&#039;s still O(n!), but it no longer requires blocking on results which aren&#039;t there yet.  I didn&#039;t go into this too deeply, but Apocalisp made it the main focus of his article.  I refer you to his superior explanations.  :-)</description>
		<content:encoded><![CDATA[<p>@Johannes</p>
<p>All true (well, see next paragraph).  The trouble is that algorithms which are intuitively parallelizable do not grow on trees.  Worse, they tend to be a little more complicated than a simple factorial and fibonacci.  I could have used them, but to do so would have detracted from the main point and made the final result a little less convincing.  14.6 vs 7.5 seconds is a pretty impressive comparison, and while the ratio would have been the same with a O(n) fibonacci, the difference would not have been so dramatic.</p>
<p>I actually disagree with you that the naive implementation of Fibonacci is not parallelizable.  The fact that each calculation depends on two previous ones does not necessarily obviate the possibility, as shown by the fact that I did it.  <img src='http://www.codecommit.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />   Doug Lea himself in explaining fork/join used pseudo-code algorithms which are assumed to be recursive in this fashion, despite the fact that they would share the same &#8220;unparallelizable&#8221; characteristics.</p>
<p>I think what you are seeing in Fibonacci is the issue of blocking recursion, each computation requiring its precedents and blocking until they are available.  This is the problem which is solved by using actors in a linear chain.  Each computation completes and then passes the result *specifically* to another Promise.  Essentially, the actor strategy used transforms the Fibonacci computation from top-down to bottom-up.  It&#8217;s still O(n!), but it no longer requires blocking on results which aren&#8217;t there yet.  I didn&#8217;t go into this too deeply, but Apocalisp made it the main focus of his article.  I refer you to his superior explanations.  <img src='http://www.codecommit.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Johannes Rudolph</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4067</link>
		<dc:creator>Johannes Rudolph</dc:creator>
		<pubDate>Sun, 28 Sep 2008 12:12:24 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4067</guid>
		<description>Hi Daniel,

thx for the step-by-step intro to the fork/join principle.

Like Jonathan, I think your choice of functions is a little bit misleading. Factorial is perfectly parallelizable by using the right algorithm. (You said that would require an &#039;entirely different approach&#039;, but isn&#039;t using fork/join a different approach, as well?) . Admittedly the resulting definition of factorial is not as nice as your simple mathematical definition.

The fibonacci function however is in a very deep sense not parallelizable, since the last result depends on every single result before. Looking at the whole tree of computations your algorithm does, you can see that you call fib O(n!) times. Than you cut that in half by using two threads. You noted yourself that there are better algorithms: &#039;Also, they’re fairly efficient in their own rights and thus see far less benefit from parallelization at the end of the day.&#039;. That&#039;s right, because you can&#039;t reasonably make it parallel.

Don&#039;t take me wrong, I know the purpose of this post was to explain fork/join but it leaves a thought of premature optimization to me.

Here&#039;s a linear version which just remembers the last two elements:
def fib(i:Long):Long = {                                                         
   def fib2(i0:Long,i1:Long,to:Long):Long = 
      if (to==1) i0 else fib2(i1,i0+i1,to-1)
   fib2(1,1,i)
}

And here the parallelizable fac:
def fac(i:Int):Long = {
  def inner(start:Int,length:Int):Long = {
     val p = length/2
     if (length == 1) start else inner(start,p) * inner(start+p,length-p)
  }
  inner(1,i)
}</description>
		<content:encoded><![CDATA[<p>Hi Daniel,</p>
<p>thx for the step-by-step intro to the fork/join principle.</p>
<p>Like Jonathan, I think your choice of functions is a little bit misleading. Factorial is perfectly parallelizable by using the right algorithm. (You said that would require an &#8216;entirely different approach&#8217;, but isn&#8217;t using fork/join a different approach, as well?) . Admittedly the resulting definition of factorial is not as nice as your simple mathematical definition.</p>
<p>The fibonacci function however is in a very deep sense not parallelizable, since the last result depends on every single result before. Looking at the whole tree of computations your algorithm does, you can see that you call fib O(n!) times. Than you cut that in half by using two threads. You noted yourself that there are better algorithms: &#8216;Also, they’re fairly efficient in their own rights and thus see far less benefit from parallelization at the end of the day.&#8217;. That&#8217;s right, because you can&#8217;t reasonably make it parallel.</p>
<p>Don&#8217;t take me wrong, I know the purpose of this post was to explain fork/join but it leaves a thought of premature optimization to me.</p>
<p>Here&#8217;s a linear version which just remembers the last two elements:<br />
def fib(i:Long):Long = {<br />
   def fib2(i0:Long,i1:Long,to:Long):Long =<br />
      if (to==1) i0 else fib2(i1,i0+i1,to-1)<br />
   fib2(1,1,i)<br />
}</p>
<p>And here the parallelizable fac:<br />
def fac(i:Int):Long = {<br />
  def inner(start:Int,length:Int):Long = {<br />
     val p = length/2<br />
     if (length == 1) start else inner(start,p) * inner(start+p,length-p)<br />
  }<br />
  inner(1,i)<br />
}</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Steve Jenson</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4065</link>
		<dc:creator>Steve Jenson</dc:creator>
		<pubDate>Fri, 26 Sep 2008 18:40:21 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4065</guid>
		<description>Not sure if you&#039;re familiar with it, but the E language has a version of Futures called Promises as well.

http://www.erights.org/elib/distrib/pipeline.html
http://www.erights.org/elib/concurrency/refmech.html

Love the write-up, BTW.</description>
		<content:encoded><![CDATA[<p>Not sure if you&#8217;re familiar with it, but the E language has a version of Futures called Promises as well.</p>
<p><a href="http://www.erights.org/elib/distrib/pipeline.html" rel="nofollow">http://www.erights.org/elib/distrib/pipeline.html</a><br />
<a href="http://www.erights.org/elib/concurrency/refmech.html" rel="nofollow">http://www.erights.org/elib/concurrency/refmech.html</a></p>
<p>Love the write-up, BTW.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Daniel Spiewak</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4062</link>
		<dc:creator>Daniel Spiewak</dc:creator>
		<pubDate>Wed, 24 Sep 2008 04:40:33 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4062</guid>
		<description>On second thought, I&#039;m a howling jay.  The function I gave in my comment does *not* compute factorial, it does something else entirely.  My point remains though: a compartmentalized factorial as Jonathan described cannot be written as nicely recursively as the canonical implementation.</description>
		<content:encoded><![CDATA[<p>On second thought, I&#8217;m a howling jay.  The function I gave in my comment does *not* compute factorial, it does something else entirely.  My point remains though: a compartmentalized factorial as Jonathan described cannot be written as nicely recursively as the canonical implementation.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Daniel Spiewak</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4061</link>
		<dc:creator>Daniel Spiewak</dc:creator>
		<pubDate>Wed, 24 Sep 2008 00:27:51 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4061</guid>
		<description>@Jonathan

Yes, you can split the factorial up into segments and compute them separately, but that&#039;s an entirely different approach.  I probably should have said &quot;there is no way to modify *this* implementation...&quot;  The recursive factorial function only sees two numbers at a time, its current index and whatever the result is from its recursive invocation.  A better (but far more complicated) implementation of factorial which could be parallelized using fork/join might look like this:

def fac(n: Int) = if (n &lt; 2) 1 else {
{ (_:Int) * (_:Int) * (_:Int) } =&lt;&lt; promise(n) =&lt;&lt; fac(n - 1) =&lt;&lt; fac(n - 2)
}

At first glance, it seems like this is the same as your suggested implementation.  However, we have introduced a very subtle problem here: overlapping sub-problems.  Think about it, fac(n) requires fac(n - 1) and fac(n - 2).  Moving on, fac(n - 1) requires fac(n - 2) and fac(n - 3).  This naive implementation has increased the computational complexity from O(n) to O(n!).  Aside from the neatly-recursive nature of that expression, we&#039;ve created a mess.

In order to make the &quot;piecemeal factorial&quot; work, we either need to memoize the results (dynamic programming), or we need to try a different approach.  Specifically, we could start from the bottom and work our way upwards to n.  Unfortunately, this implementation would not be naturally recursive and thus, a little bit less subjectively elegant.  :-)

So unwinding that nice, long rabbit trail: yes, you&#039;re correct, factorial *can* be computed in parallel.  However, it can be very difficult to explain that in short, comprehensible examples without leading to a distracting tangent like the above.  Besides, it was sort of nice and simple to explain operation reordering just using fac and fib, two functions that everyone understands intuitively.</description>
		<content:encoded><![CDATA[<p>@Jonathan</p>
<p>Yes, you can split the factorial up into segments and compute them separately, but that&#8217;s an entirely different approach.  I probably should have said &#8220;there is no way to modify *this* implementation&#8230;&#8221;  The recursive factorial function only sees two numbers at a time, its current index and whatever the result is from its recursive invocation.  A better (but far more complicated) implementation of factorial which could be parallelized using fork/join might look like this:</p>
<p>def fac(n: Int) = if (n < 2) 1 else {<br />
{ (_:Int) * (_:Int) * (_:Int) } =&lt;&lt; promise(n) =&lt;&lt; fac(n &#8211; 1) =&lt;&lt; fac(n &#8211; 2)<br />
}</p>
<p>At first glance, it seems like this is the same as your suggested implementation.  However, we have introduced a very subtle problem here: overlapping sub-problems.  Think about it, fac(n) requires fac(n &#8211; 1) and fac(n &#8211; 2).  Moving on, fac(n &#8211; 1) requires fac(n &#8211; 2) and fac(n &#8211; 3).  This naive implementation has increased the computational complexity from O(n) to O(n!).  Aside from the neatly-recursive nature of that expression, we&#8217;ve created a mess.</p>
<p>In order to make the &#8220;piecemeal factorial&#8221; work, we either need to memoize the results (dynamic programming), or we need to try a different approach.  Specifically, we could start from the bottom and work our way upwards to n.  Unfortunately, this implementation would not be naturally recursive and thus, a little bit less subjectively elegant.  <img src='http://www.codecommit.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>So unwinding that nice, long rabbit trail: yes, you&#8217;re correct, factorial *can* be computed in parallel.  However, it can be very difficult to explain that in short, comprehensible examples without leading to a distracting tangent like the above.  Besides, it was sort of nice and simple to explain operation reordering just using fac and fib, two functions that everyone understands intuitively.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonathan</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4060</link>
		<dc:creator>Jonathan</dc:creator>
		<pubDate>Wed, 24 Sep 2008 00:12:29 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4060</guid>
		<description>Perhaps I misunderstand what you mean by &quot;there is no way to modify the implementation of the factorial function in a parallel fashion&quot;, but... to work out 9!, why can&#039;t I (on my 3 processors) work out 1*2*3, 4*5*6, and 7*8*9 in parallel, then multiply the results together?</description>
		<content:encoded><![CDATA[<p>Perhaps I misunderstand what you mean by &#8220;there is no way to modify the implementation of the factorial function in a parallel fashion&#8221;, but&#8230; to work out 9!, why can&#8217;t I (on my 3 processors) work out 1*2*3, 4*5*6, and 7*8*9 in parallel, then multiply the results together?</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Apocalisp</title>
		<link>http://www.codecommit.com/blog/scala/higher-order-fork-join-operators/comment-page-1#comment-4059</link>
		<dc:creator>Apocalisp</dc:creator>
		<pubDate>Tue, 23 Sep 2008 18:27:21 +0000</pubDate>
		<guid isPermaLink="false">http://www.codecommit.com/blog/scala/higher-order-fork-join-operators#comment-4059</guid>
		<description>&quot;I still don’t see how the “map a promise-valued function over another” can be concurrent.&quot;

It isn&#039;t really, but the point there is that you don&#039;t need to construct a hierarchy of promises. Because of monadic join, what you end up with is a single actor waiting for a value to which to apply the composed function.

The &quot;bind&quot; method that I&#039;m referencing in my article is equivalent to liftM2 also. It&#039;s a composition of apply and map, and apply is in turn just a composition of bind and map.

Have fun brushing up on your Haskell.</description>
		<content:encoded><![CDATA[<p>&#8220;I still don’t see how the “map a promise-valued function over another” can be concurrent.&#8221;</p>
<p>It isn&#8217;t really, but the point there is that you don&#8217;t need to construct a hierarchy of promises. Because of monadic join, what you end up with is a single actor waiting for a value to which to apply the composed function.</p>
<p>The &#8220;bind&#8221; method that I&#8217;m referencing in my article is equivalent to liftM2 also. It&#8217;s a composition of apply and map, and apply is in turn just a composition of bind and map.</p>
<p>Have fun brushing up on your Haskell.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
