Sunday, August 3, 2014

Web Workers in practice - Integration (HTML5)

Intro

Couple of years ago, web workers (along with other HTML5 stuff) suddenly became popular. On the  spike of their popularity, many tutorials were written. However, the moment of popularity was very brief, mainly because, well, you don't need this in your regular JS application. The common use case - blocked UI because of long-running script - is rather rare if you use modern browser with fast JS engine.
Have you really seen something like that recently?
In lots of tutorials all the workers did was passing messages to main script, where it was printed to console or put in some DOM element, which doesn't give you any feeling of concurrency at all.
More advanced ones included prime number sequence generation, which was more like it, as this is a really long operation, especially when you generate a long sequence :)
In this tutorial I'll try to show another example how web workers could be useful in practice - in other math problem.



Monte-Carlo integration

Good description of numerical integration with Monte Carlo can be found here. I'm not good at math analysis (really), but I think this concept is rather easy to grasp. In short, definite integral of the function equals to area under the graph of function.
The method described in the paper linked above - which is the one I use - has 2 important limitations:

1. You should know the max. value of the function in the area (rectangle) you've taken.

2. The rectangle lower limits for both axis are 0. So the lower limit for the definite integral (x1) will always be 0.

I'm more than sure that at least 2nd limitation can be worked around. But at the moment I'm too lazy for it.
Visual representation of definite integral

Implementation

If you've read the paper, you saw that the idea is simple: we generate 2 pseudo-random values in the following ranges - 0..Xmax (xR) and 0..Fmax (fR), then we test if fR <= f(xR). If it is, point is accepted, so we increment the accepted points counter.
E.g., how it looks for Cos^2x function:

var CosPowerTwoX = (function(){
 return {
  isPointAccepted: function(fR, xR){   
   return fR <= (Math.cos(xR) * Math.cos(xR));
  }
 };
})();

As you may've guessed, the more trials we perform, the more accurate is the result. In the paper they talk about 1,000 to 1,000,000 trials; I ended up performing 30,000,000 trials, as 1,000,000 trials  ran blazingly fast even in "single-thread" implementation :)
Result is calculated as follows:
(accepted/TRIAL_COUNT)*(fMax*xMax);
We just calculate how much of the rectangle area is taken by function.

Web workers

I dislike the straightforward web worker initiation where worker is loaded from a separate script file, because it doesn't work when running locally (for security reasons) and you can't run it in jsFiddle or Codepen. So I used other method described in HTML5 rocks tutorial, putting worker code in script element on page.
It looks like that:

 var blob = new Blob([document.querySelector('#worker1').textContent]);
 workers.push(new Worker(window.URL.createObjectURL(blob)));
 
 workers[i].onmessage = function(e) {
  
  if (e.data === 'finished')
   finishedWorkers++;
  else
   accepted += e.data;
 }
 
 workers[i].postMessage({ cmd: 'start', 'xMax' : xMax, 'yMax': yMax, 'trialCount': WORKER_TRIAL_COUNT }); 

So there you have it. In current implementation worker sends 2 messages; one message with JSON would look nicer - will fix that later.

Results

The concurrent version is notably faster - takes ~1.5s in Firefox 31 (Win8). Even faster in Chromium 34 - about 1s.

Whereas non-concurrent version takes ~2,2s in Chromium 34 aaaand... "unresponsive script" alert in Firefox (when Firebug console's opened)!

 

Source code

You can get the code from github. I will add the demo later if I'm not too lazy.

No comments:

Post a Comment