title: Performance of the compiled vs. interpreted javascript in java7 / Rhino
body: <p>I have a problem with performance of Rhino javascript engine in Java7, shortly - my script (that parses and compiles texts) runs in Chrome around 50-100 times quicker than the same in Java7 Rhino script engine.</p>
  
  <p>I was trying to find the way to improve situation and have found that Rhino supports compilation of scripts. I tried doing it with my scripts and actually did not see any improvement. Finally - i ended up with a dummy short test suite where i do not see any difference in performance between compiled and interpreted versions. Please let me know what I'd doing wrong.</p>
  
  <p>Note: some sources mention that Rhino engine runs compiled script roughly 1.6 slower than the "same" code written directly in Java. Not sure if "compilation of script" used in this sample the is same one which is supposed there.</p>
  
  <p>Test java class is below and sample result I'm getting from it on my machine ...</p>
  
  <p><b>Results</b><br/></p>
  
  <pre>
       Running via com.sun.script.javascript.RhinoScriptEngine@c50443 ... 
        time: 886ms, chars: 38890, sum: 2046720
        time: 760ms, chars: 38890, sum: 2046720
        time: 725ms, chars: 38890, sum: 2046720
        time: 765ms, chars: 38890, sum: 2046720
        time: 742ms, chars: 38890, sum: 2046720
         ... 3918ms
  
  
       Running via com.sun.script.javascript.RhinoCompiledScript@b5c292 @ com.sun.script.javascript.RhinoScriptEngine@f92ab0 ... 
        time: 813ms, chars: 38890, sum: 2046720
        time: 805ms, chars: 38890, sum: 2046720
        time: 812ms, chars: 38890, sum: 2046720
        time: 834ms, chars: 38890, sum: 2046720
        time: 807ms, chars: 38890, sum: 2046720
         ... 4101ms
  </pre>
  
  <p><b>Update</b> after comment from Anon-Micro: <br></p>
  
  <p>After wrapping  invocation of the JavaScript eval() and compile() in test class into ...</p>
  
  <pre><code>import sun.org.mozilla.javascript.internal.Context;
  try {
      Context cx = Context.enter();
  
      cx.setOptimizationLevel(9);
      cx.setLanguageVersion(170);
  
      ...
  }
  finally {
      Context.exit();
  }
  </code></pre>
  
  <p>result changed signigicantly - from average 1.8 (in new version of test class) sec to ~150msec. However instance of the doTest() function extracted from ScriptEngine loaded via <code>(CompiledScript = Compilable.compile()).eval(Bindings) -> Bindings.get("doTest")</code> still says it is <code>sun.org.mozilla.javascript.internal.InterpretedFunction</code> and its performance is slightly worse (around 10%) than version of JS loaded from pre-compiled bytecode (by Rhino 1.7r4) - so i'm still not sure what is actually happening behind the scene.</p>
  
  <pre><code>1800ms - ScriptEngine.eval(), Optimization Level = default(-1?)
  1758ms - CompiledScript, Optimization Level = default(-1?)
   165ms - ScriptEngine.eval(), Optimization Level = 9
   132ms - CompiledScript, Optimization Level = 9
   116ms - compiled by Rhino 1.7r4 into bytecode class
  </code></pre>
  
  <p>PS: sun.org.mozilla.javascript.internal.Context within internal sun's package looks to be a weird design for me - 'internal' denotes this class is assumed not to be used by developers and therefor there is not 'certified' way to manipulate optimization level of JS evaluator in Java 7.</p>
  
  <p><b>Test class (updated, doTestCompiled is loaded from external *.class)</b><br/></p>
  
  <pre><code>import javax.script.Bindings;
  import javax.script.Compilable;
  import javax.script.CompiledScript;
  import javax.script.Invocable;
  import javax.script.ScriptContext;
  import javax.script.ScriptEngine;
  import javax.script.ScriptEngineManager;
  import javax.script.SimpleScriptContext;
  import sun.org.mozilla.javascript.internal.Context;
  import sun.org.mozilla.javascript.internal.Scriptable;
  import sun.org.mozilla.javascript.internal.Function;
  
  public class RhinoPerfTest4 {
  
      final static ScriptEngineManager scm = new ScriptEngineManager();
      final static String TEST_SCRIPT1 =
              "function doTest() {\n"
              + "    var scale = 5000, i, a = [], str, l, sum = 0,\n"
              + "        start = (new Date()).getTime(), end;\n"
              + "    for( i = 0; i &lt; scale; i++ )\n"
              + "        a.push(\"\" + i);\n"
              + "    str = a.join(\"\");\n"
              + "    l = str.length;\n"
              + "    for( i = 0; i &lt; l; i++ ) {\n"
              + "        var c = str.charCodeAt(i);\n"
              + "        if( c &gt; 0)\n"
              + "            sum += c;\n"
              + "    }\n"
              + "    end = (new Date()).getTime();\n"
              + "\n"
              + "    // print(\" time: \" + (end - start) "
              + "          + \"ms, chars: \" + l "
              + "          + \", sum: \" + sum + \"\\n\");\n"
              + "}\n";
      final static String TEST_SCRIPT2 =
              "function doTest() {\n"
              + "    var a = [], i;\n"
              + "    for( i = 0; i &lt; 500; i++ ) a.push(1);\n"
              + "}\n";
  
      static class TestSet {
  
          public int nCycles;
          public String script;
  
          public TestSet(int nCycles, String script) {
              this.nCycles = nCycles;
              this.script = script;
          }
      }
      static TestSet set1 = new TestSet(5, TEST_SCRIPT1);
      static TestSet set2 = new TestSet(500, TEST_SCRIPT2);
  
      public static void main(String[] args) throws Exception {
          ScriptEngine se;
          int i;
          long ts, te;
          TestSet set = set1;
          Object noArgs[] = new Object[]{};
  
          try {
              org.mozilla.javascript.Context mctx = org.mozilla.javascript.Context.enter();
  
              se = scm.getEngineByExtension("js");
              doTestCompiled doTestPreCompiled = new doTestCompiled();
              org.mozilla.javascript.Scriptable scope = mctx.initStandardObjects();
  
              doTestPreCompiled.call(mctx, scope, scope, null);
              org.mozilla.javascript.Function doTest = 
                      (org.mozilla.javascript.Function)scope.get("doTest", null);
  
              for( int nHotSpot = 0; nHotSpot &lt; 5; nHotSpot++ ) {
                  if( nHotSpot &gt; 0 )
                      Thread.sleep(500);
  
                  ts = System.currentTimeMillis();
                  for( i = 0; i &lt; set.nCycles; i++ ) {
                      doTest.call(mctx, scope, null, null);
                  }
                  te = System.currentTimeMillis();
                  System.out.println("  " + nHotSpot + ": " + (te - ts) + "ms");
              }
          }
          finally {
              org.mozilla.javascript.Context.exit();
          }
  
  
          for( int nOpt = 0; nOpt &lt; 2; nOpt++ ) {
              if( nOpt &gt; 0 )
                  Thread.sleep(500);
  
              Context cx = null;
  
              try {
                  System.out.println("Cycle: " + nOpt);
  
                  cx = Context.enter();
                  if( nOpt &gt; 0 ) {
                      System.out.println("OptLevel: " + 9);
                      cx.setOptimizationLevel(9);
                      cx.setLanguageVersion(170);
                  }
  
                  se = scm.getEngineByExtension("js");
                  se.eval(set.script);
                  System.out.println("\nRunning via " + se + " ... ");
  
                  Invocable invocable = (Invocable) se;
  
                  for( int nHotSpot = 0; nHotSpot &lt; 5; nHotSpot++ ) {
                      if( nHotSpot &gt; 0 )
                          Thread.sleep(500);
  
                      ts = System.currentTimeMillis();
                      for( i = 0; i &lt; set.nCycles; i++ ) {
                          invocable.invokeFunction("doTest", noArgs);
                      }
                      te = System.currentTimeMillis();
                      System.out.println("  " + nHotSpot + ": " + (te - ts) + "ms");
                  }
  
                  se = scm.getEngineByExtension("js");
                  Compilable cse = (Compilable) se;
                  CompiledScript cs = cse.compile(set.script/* + "(doTest())"*/);
                  Scriptable scope = cx.initStandardObjects();
  
                  ScriptContext scriptContext = new SimpleScriptContext();
                  Bindings vars = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
  
                  cs.eval(vars);
  
                  Object odoTest = scriptContext.getAttribute("doTest");
                  Function doTest = (Function) vars.get("doTest");
  
                  System.out.println("\nRunning via " + cs + " @ " + se + " ... ");
  
                  for( int nHotSpot = 0; nHotSpot &lt; 5; nHotSpot++ ) {
                      if( nHotSpot &gt; 0 )
                          Thread.sleep(500);
  
                      ts = System.currentTimeMillis();
                      for( i = 0; i &lt; set.nCycles; i++ ) {
                          doTest.call(cx, scope, null, noArgs);
                      }
                      te = System.currentTimeMillis();
                      System.out.println("  " + nHotSpot + ": " + (te - ts) + "ms");
                  }
  
              }
              finally {
                  if( cx != null )
                      Context.exit();
              }
          }
      }
  }
  </code></pre>