Is Nashorn (JVM) faster than Node (V8)?

The answer to the question “Is Nashorn (JVM) faster than Node (V8)?” is in most people’s minds: a foregone conclusion, looking something like this expression.

no-way-9yykt8

It certainly was in my mind, until I actually ran a very simple benchmark that computes the Fibonacci sequence a few times (recursively). It’s a common enough benchmark, one frequently used to test method dispatching, recursion and maths in various languages / virtual machines. For disclosure, the code is listed below:

function fib(n) {
  if (n < 2)
    return 1;
  return fib(n - 2) + fib(n - 1);
}

try {
  print = console.log;
} catch(e) {
}

for(var n = 30; n <= 50; n++) {
  var startTime = Date.now();
  var returned = fib(n);
  var endTime = Date.now();
  print(n + " = " + returned + " in " + (endTime - startTime));
}

The results are, lets just say: “shocking”. The table below is both runs along side each other, the last numbers are the important ones: how many milliseconds each run took.

jason@Bender ~ $ node fib.js 
30 = 1346269 in 12
31 = 2178309 in 17
32 = 3524578 in 27
33 = 5702887 in 44
34 = 9227465 in 69
35 = 14930352 in 113
36 = 24157817 in 181
37 = 39088169 in 294
38 = 63245986 in 474
39 = 102334155 in 766
40 = 165580141 in 1229
41 = 267914296 in 2009
42 = 433494437 in 3241
43 = 701408733 in 5302
44 = 1134903170 in 8671
45 = 1836311903 in 13626
46 = 2971215073 in 22066
47 = 4807526976 in 45589
48 = 7778742049 in 74346
49 = 12586269025 in 120254
50 = 20365011074 in 199417
jason@Bender ~ $ jjs -ot fib.js 
30 = 1346269 in 70
31 = 2178309 in 16
32 = 3524578 in 19
33 = 5702887 in 30
34 = 9227465 in 48
35 = 14930352 in 76
36 = 24157817 in 123
37 = 39088169 in 197
38 = 63245986 in 318
39 = 102334155 in 517
40 = 165580141 in 835
41 = 267914296 in 1351
42 = 433494437 in 2185
43 = 701408733 in 3549
44 = 1134903170 in 5718
45 = 1836311903 in 9306
46 = 2971215073 in 15031
47 = 4807526976 in 34294
48 = 7778742049 in 38446
49 = 12586269025 in 61751
50 = 20365011074 in 100343

So what on earth is going on here? How can it be that the last result is 99 seconds faster on the Java VM than on V8, one of the fastest JavaScript engines on the planet?

The answer is actually hidden at the top of the tables: the -ot option being passed to JJS (Java JavaScript) is the key to this magic sauce. OT stands for Optimistic Typing, which aggressively assumes that variables are all compatible with the Java int type, and then falls back to more permissive types (like Object) when runtime errors happen. Because the code above only ever produces int values, this is a very good assumption to make and allows the Java VM to get on and optimise the code like crazy, where V8 continues to chug along with JavaScript’s Number type (actually it is more intelligent than that, but it’s just not showing in this little benchmark).

The point of this post is: don’t believe everything you read in benchmarks. They are very dependant on the code being run, and micro-benchmarks are especially dangerous things. The work that has been done on Optimistic Typing in Nashorn is amazing, and makes it a very attractive JavaScript environment. But you shouldn’t believe that just because these benchmarks show such a wide gap in performance that your Express application would run faster in Nashorn than under V8… In fact you’d be lucky to get it to run at all.

Feel free to replicate this experiment on your machine. It’s amazing to watch, it surprises me every time I re-run it.