Jun 17, 2013

JavaScript on the JVM: experimenting with Nashorn

I recently experimented with the upcoming Nashorn JavaScript run-time to compile Less style sheets.
I hoped to boost the performance in comparison with existing Less compilers for the JVM, but the results where not what I expected...


Although I am a fan of Twitter Bootstrap and Less CSS in general, I found that the Less compilation in Java projects could take several seconds and thus be really bad for development turnaround.
During Devoxx 2012, Marcus Lagergren explained how much faster Nashorn was compared with Rhino (being already included in the standard Java distribution). So I took it for a test drive.

Can't wait for Java 8

 I didn't have JDK8 installed yet, so I forked a backport on GitHub and build the nashorn.jar that provides a standard javax.script.ScriptEngine implementation.
This made it easy to support both Rhino and Nashorn: by specifying -Djava.ext.dirs=/path/to/nashorn, Nashorn was used by default with a fallback to Rhino.

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine scriptEngine = factory.getEngineByName("nashorn");
if (scriptEngine == null) {
    scriptEngine = factory.getEngineByName("nashorn");
if (scriptEngine == null) {
    throw new RuntimeException("No JavaScript script engine found");

The compiler

I didn't use the Rhino specific less script, but instead used the standard less-1.3.3.js. I only had to add a few lines of JavaScript in front, to stub a little browser functionality, and add a compile function at the back to be called from Java.
The Java code was rather simple:
String css =
   ((Invocable) scriptEngine).invokeFunction("compile", lessString);

The Bootstrap test

The Twitter Bootstrap style sheets compiled successfully with both Rhino and Nashorn
Compilation with Rhino took 10 seconds on my Macbook, while Nashorn did in in 8 seconds. Not exactly impressive...
To get an idea of the JavaScript compilation / execution ratio , I invoked the Less compile function multiple times, hereby reusing the JavaScript context.

Time needed to compile Twitter Bootstrap (seconds)
  The first row includes JavaScript compilation
# Rhino Rhino
1 9.8 5.5 8.0 15.0
2 7.5 2.1 2.5 5.8
3 7.3 1.5 / 3.7
4 7.4 1.1 / 3.4
5 7.3 1.0 / 3.4

The first column is for Rhino invoked as described above using the javax.script interfaces. By default Rhino always runs in interpreted mode. Compare this with the second column, where the Rhino Context is used directly which allows to set the optimization level to 9 (highest level).
The third column is for Nashorn on Java 7. The Nashorn backport apparently contains some bugs, causing the third Less compilation within the same JavaScript context to fail.
The fourth column contains the results of the Less compilation using a JDK8 snapshot. The engine now works correctly, but the performance still needs to improve a lot.


I realize that the Nashorn project is still in alpha, but until the team comes up with an API that allows compiler optimizations like Rhino does, the latter still seems to be the best bet.