Thursday, December 24, 2009

Promises of Parallel in Perl6


Been reading through the Perl6 specs again.  I like the way the synopses make "promises of parallelizability" on three families: junctions, hyperops, and feeds.  I also like the way these three promises differ from one another.  This post is a little thought experiment, in which I will act as the interpreter for all three.


Junctions
Of the three, junctions are the most difficult for me to grok.  Junctions are a family of four: all()any()none(), and one().  One obvious place where this family of four comes in handy:
say "foo" if all(1,2,3) # says foo 
The semantics, in this case, are obvious.  In Boolean context, all evaluates to True if all of the arguments are true, any if any are true, none if (and only if) none are true, and one if exactly one is true.  In this case, it's meant as a nice shorthand for:
say "foo" if (1 && 2 && 3) # says foo
 Except under the covers, it's much different (or at least I suspect it is, inside the brain of Larry Wall).  Since junctions carry a promise of autothreading, the former is a way of handing off to three threads in any order, and the latter (probably) executes in a promised order, (probably) with short-circuiting.  Concentrating on the former, it's not that interesting the way it's written, so let's imagine it a bit differently:
say "foo" if any(bar(1), bar(2), bar(3));
Basically, what I imagine this to be saying is, "go execute bar() in wherever it is convenient, in boolean context, and return the result here.  I will continue whenever either: a) one of you comes back true, or b) all of you come back false."  In fact, you can imagine doing something like this:
do_stuff() if any(breadthfirst($tree), depthfirst($tree));
This could be shorthand for: "do a breadth first and a depth first at the same time, and whenever one of you returns True, the other one can just stop."  It's a very nice kind of syntax in a world where we have a few processors to play around with.

Hyperops
Hyperoperators differ slightly from junctions- they still promise autothreading, but they cannot short-circuit in the same way a junction can.  Hyperops, instead, execute a block-alternatively-statement (this, in Perl6 is called a "blast" I think) on every single member of a list.  It looks like this:
[1,2,3]>>.say # prints 1\n2\n3\n
 Once again, it's a pretty simple example that executes .say on every member of the list.  This, under the covers, might be done in any order, even somewhere else.  As such, any side effects might also occur out-of-order.  I've heard that the order of results is generally preserved, but I'm not at all clear how much that matters in most cases.

Anyhow, back to the previous example, if I did something like [1,2,3]>>.bar(), it would promise to execute bar() three times with three topics (1,2, and 3), side effects would be preserved with all three.  One can imagine how this would be useful for building cellular automata, where one has a grid to keep in synch, and one might do something like @grid>>.next() to yield a subsequent state.

Feeds
Feeds are autothreading just like the previous two, but instead of promising that all will be executed, or short-circuiting, the control of the timing of the autothreading is left to the context (caller?).  A feed looks something like this:
my $a,$b,$c <== 0..Inf
 That <== thing is the feed.  Basically, what this does is says "ask my pointy side how many it wants, grab that many from my blunt end when my pointy end needs them, execute them in any order, anywhere, but give them back to the pointy end in the order they were asked for."  It's not short-circuiting like a junction, it's doesn't guarantee execution on the whole list like a hyperop, but it does provide some pretty nuanced control when combined with either itself or its friends.


I'm still thinking about it, but this increasingly seems to be a nice approach to parallelization, which has good dwimmery (e.g. if you're doing hypers and junctions, even if you weren't trying to autothread, it probably does what you meant), nice fine-grained control (execute all, execute some number, execute with short-circuiting), and still mixes nicely with other parts of the language.

I'm not sure if there are other parallel constructions either already in the spec or on their way in, but I don't find any immediate holes in the semantics here.  Some additional control over how and where the processes are spread around might be achieved with some introspection on the current context, which may require some additional syntax, but that may also be left as an exercise for the reader.

Labels: , ,


Comments: Post a Comment

Subscribe to Post Comments [Atom]





<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]