croczilla.com 
 home   stratified   bits&pieces   blog   
  home > blog > Stratified JavaScript and Multitasking
10 September
2009

Stratified JavaScript and Multitasking

[oni] 

In response to my earlier blog post on Stratified JavaScript, Jonathan Cook asked an interesting question:

"[...] I am very interested in using SJS to eliminate the blocking
nature of heavy computation in nested loops.

A hold(0) would seem to do this, or perhaps a different method for
this purpose would be appropriate?"

The short answer is, you don't need to call hold() or do anything special at all. Just code your computation as you would in 'normal' JavaScript, and SJS will take care of the rest; the UI will automatically stay responsive while your computation is chugging along.

Now for the slightly longer answer.

The SJS runtime contains a scheduler which preemptively schedules the different strands of execution ('strata') of an SJS program. This scheduler automatically yields control to the browser every so often, and it ensures that individual strata get a fair timeslice.

In other words, SJS implements a form of preemptive multitasking, rather than cooperative multitasking. There is no need to manually yield control like you would in e.g. Narrative JavaScript/Strands; SJS automatically does it in the background.

Here's an example of a heavy computation in SJS; a program to calculate a few hundred digits of Pi:
pi.html

There are a couple of noteworthy things about this program:

Firstly, if you look at the source, there is no hold() or any other special SJS primitive. It is just 'straight' JS code, which is being preprocessed through the SJS compiler by virtue of sitting in a script tag with type "text/sjs".

Secondly, it runs painfully slow. This is a consequence of the fact that the SJS compiler doesn't perform any optimizations at all at this point; every JavaScript function call or operator application is converted into a potential yielding point, adding huge overhead to programs which are heavy on mathematical calculations.

This situation will improve dramatically when we get around to making the compiler smarter. A similar optimization in Oni resulted in a 2 orders of magnitude improvement!

In the meanwhile there is a workaround though: SJS code can freely call 'normal' JavaScript code, so we can just move critical inner loops into 'normal' JavaScript, as in this modified program:
pi-faster.html

You'll notice that this program runs a lot faster, while not really adding much complexity - just one inner loop has been moved to 'straight' JavaScript.

Just to prove that the preemptive scheduler works as advertised, here are another couple of examples which add some SJS-specific features.

Firstly, this program adds an animation running in parallel to the Pi calculation:
pi-and-animation.html

Secondly, and finally, this program adds a 'UI' for turning the animation on and off:
pi-and-animation-and-events.html

Take a look at the animation loop:

  function animateLoop() {
    var elem = document.getElementById("toggle_animation");
    while (true) {
      waitForEvent("click", elem);
      elem.innerHTML = "Stop animation";
      animate() @ waitForEvent("click", elem);
      elem.innerHTML = "Restart animation";
    }
  }

Isn't it beautiful? :-)


Posted by alex at 16:52 | Comments (4)
<< Introducing Stratified JavaScript | Main | Building a Chumby-style feed reader using SJS >>
Comments
Re: Stratified JavaScript and Multitasking

Thanks for the response and the great info which gives me an insight into the workings of Oni and SJS without having to read the code :)

Is it possible that you will let us set the yieldable points manually? For my application, which is a shim for Web Worker in non-supporting browsers, that would seem to be just the end of a block of code in a loop and before the end of (or return call in) a function body.

It would be easier to wrap these innards in noyield block construct such as

for (var i =0; i < 100000; i++) { noyield { ... } }


and matching yield { }

than to split up the code as you suggest (if only for reasons of scoping)

Posted by: Jonathan 'J5' Cook at September 10,2009 21:47
Re: Stratified JavaScript and Multitasking

@Jonathan:

The example in the blog post where the inner loop was moved to
JavaScript was more about demonstrating that SJS can freely interact
with 'normal' JavaScript, rather than outlining a generic
optimization strategy.

SJS is all about abstracting away the messy details of concurrency. A
'noyield' construct would run counter to this. I'm confident that in
future the SJS compiler will be smart enough to choose good yield
points by itself, rendering 'noyield' obsolete. Remember
that SJS is still at the prototyping stage. For the time being it's
not about how well the bear dances, it's that he dances at all :-)

BTW - If it is 'only' yielding you want, you should check out Strands.

Posted by: alex at September 11,2009 07:37
Re: Stratified JavaScript and Multitasking

As a side note, shouldn't the MIME type used not be application/x-sjs (for one thing, it's application code and not readable text, we're only still using text/javascript because of legacy browsers not understanding it with a correct type, and for the other, it's not officially registered, so it needs to be x-prefixed).

Posted by: Robert Kaiser at September 11,2009 21:34
Re: Stratified JavaScript and Multitasking

@Robert:

You're right, it should be application/x-sjs. I'm not quite sure if
SJS will still use the script tag based deployment mechanism when it
moves out of the prototype stage, but if yes then we'll have to fix this!

Posted by: alex at September 14,2009 19:37