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.
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.