Tuesday, September 3, 2013

Javascript vs Perl vs Python vs Lua speed comparison

I have previously compared performance of the three most popular interpreted languages - Perl, Python and Lua (part1, part2). The next question was - how do they compare to the Javascript? Unlike the above mentioned languages it was quite unusual to use Javascript outside of a browser sandbox but with developing of the node.js this became possible.

First of all the nodejs package needs to be installed. The package version in the Ubuntu repository is currently 0.6.19. This version is outdated and it's not recommended to install it because of performance reasons. To install current version (which is 0.10.17 atm) via package manager read this documentation.

I have implemented the same tests I've used before in Javascript:

1. Test for floating point numbers operations

alex@thinkpad:~/projects$ cat speedcompar/exp.js
#!/usr/bin/nodejs

var explim = 100;
var step   = 0.000001;

function myexp(val)
{
  var sum = 0.0;
  var fact = 1.0;
  var x = 1.0;

  for (var i=1;i<explim;++i)
  {
    fact*= i; 
    x*= val;
    sum+= x/fact;
  }
  return sum + 1.0; 
}

function integrate(min, max)
{
  var sum = 0.0;
  while (min<max)
  {
    sum+= myexp(min)*step; 
    min+= step;
  }
  return sum;
}

console.log('exponent( 1.0)=%d',myexp(1.0));
console.log('integral(0..1)=%d',integrate(0.0,1.0));
The result of test:
alex@thinkpad:~/projects$ time speedcompar/exp.js
exponent( 1.0)=2.7182818284590455
integral(0..1)=1.7182809693185015

real 0m0.984s
user 0m0.976s
sys 0m0.008s
The result is really impressive because it works as fast as compiled C version! Here is the updated comparison chart:

[Image]

 

2. Test for basic arithmetic operations and IO subsystem

base64 encoding algorithm implemented in Javascript:
alex@thinkpad:~$ cat bin/base64.js
#!/usr/bin/nodejs

var a = new Array();
var out = new String();
var b = 0;

function c2(a)
{
  p(a[0]>>2);p(((a[0]&3)<<4)+((a[1]&240)>>4));
}
function c3(a)
{
  c2(a);p(((a[1]&15)<<2)+((a[2]&192)>>6));
} 
function c(a)
{
  c3(a);p(a[2]&63);
}
function p(c)
{
  var n;
  if(c<26){n="A".charCodeAt(0)+c}
  else if(c<52){n="a".charCodeAt(0)+c-26}
  else if(c<62){n="0".charCodeAt(0)+c-52}
  else if(c==62){n="+".charCodeAt(0)}
  else {n="/".charCodeAt(0)}
  out+=String.fromCharCode(n);
  b+=1;
  if (b%76==0) {
    process.stdout.write(out+'\n');
    out = new String();
  }
}

function processChunk(chunk)
{
  var i = 0;
  while(i<chunk.length)
  {
    a.push(chunk[i]);
    if (a.length==3) {
      c(a);
      a = new Array();
    } 
    ++i; 
  }
}
function processEnd()
{
  if (a.length==1) {
    a.push(0);a.push(0);c2(a); 
    out+= '==';
  }
  else if (a.length==2) {
    a.push(0);c3(a);
    out+= '=';
  }
  if (b>0) {
    process.stdout.write(out+'\n');
  }
}

process.stdin.resume();
process.stdin.on('data', processChunk);
process.stdin.on('end', processEnd);
Test if implementation is correct:
alex@thinkpad:~$ dd if=/dev/urandom bs=1M count=10 > /tmp/10M
alex@thinkpad:~$ diff -s <(base64 /tmp/10M) <(cat /tmp/10M|bin/base64.js)
Files /dev/fd/63 and /dev/fd/62 are identical
Results of the measuring:
alex@thinkpad:~$ dd if=/dev/urandom bs=1M count=10 | bin/base64.js > /dev/null
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 1.9421 s, 5.4 MB/s
It still 2.5 times slower than the C implementation but 7.5 times faster than the Perl implementation! Here is the updated comparison chart:

[Image]

There's one thing worth to note: node.js stdout is not buffered and that's why I added my own buffer for a line of output. And this is the difference between Javascript and other implementations where I output one character per system function call. Perl, Python and Lua have their stdout buffered and it's flushed if overflown or if the new line character is entered. In my Javascript implementation I also flush my buffer if a new line is entered. So I think that it's fair enough to compare implementations. But if I would increase my buffer size to 512 bytes I'd get throughput up to 7.2MB/s !

2 comments:

Anonymous said...

if you're going down the node.js route (v8), you might as well compare it to luajit and pypy and whatever perl jit might exist

Anonymous said...

I think resources are an important consideration too. If you are going to discuss performance you have to discuss how much memory is used.

And BTW, I really liked this work. It considered many points of view and identified many of the approaches and their relative merits.

my 0.02