java7 rhino,Java7/Rhino中已编译与已解释JavaScript的性能

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.

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.

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.

Test java class is below and sample result I'm getting from it on my machine ...

Results

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

Update after comment from Anon-Micro:

After wrapping invocation of the JavaScript eval() and compile() in test class into ...

import sun.org.mozilla.javascript.internal.Context;

try {

Context cx = Context.enter();

cx.setOptimizationLevel(9);

cx.setLanguageVersion(170);

...

}

finally {

Context.exit();

}

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 (CompiledScript = Compilable.compile()).eval(Bindings) -> Bindings.get("doTest") still says it is sun.org.mozilla.javascript.internal.InterpretedFunction 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.

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

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.

Test class (updated, doTestCompiled is loaded from external *.class)

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 < scale; i++ )\n"

+ " a.push(\"\" + i);\n"

+ " str = a.join(\"\");\n"

+ " l = str.length;\n"

+ " for( i = 0; i < l; i++ ) {\n"

+ " var c = str.charCodeAt(i);\n"

+ " if( c > 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 < 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 < 5; nHotSpot++ ) {

if( nHotSpot > 0 )

Thread.sleep(500);

ts = System.currentTimeMillis();

for( i = 0; i < 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 < 2; nOpt++ ) {

if( nOpt > 0 )

Thread.sleep(500);

Context cx = null;

try {

System.out.println("Cycle: " + nOpt);

cx = Context.enter();

if( nOpt > 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 < 5; nHotSpot++ ) {

if( nHotSpot > 0 )

Thread.sleep(500);

ts = System.currentTimeMillis();

for( i = 0; i < 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 < 5; nHotSpot++ ) {

if( nHotSpot > 0 )

Thread.sleep(500);

ts = System.currentTimeMillis();

for( i = 0; i < 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();

}

}

}

}

解决方案

The Rhino engine is actually capable of compiling scripts into bytecode 'in-process', so you don't need to run the tool to generate .class files first. You only need to set 'optimisation level' and the engine will automatically pre-compile the script before executing it. One way to override the optimisation level is with the VM argument -Drhino.opt.level. Set this to anything between 0 and 9 and run your original test program and you should see better performance.

This is the same optimisation setting used by the compiling tool you mentioned, by the way. https://developer.mozilla.org/en-US/docs/Rhino/Optimization

For total control of optimisation level and javascript version within your program, you can do the following. However you lose some of the trimmings that RhinoScriptEngine class (which is just a environment wrapper and not the javascript engine) provides. One such trimming is the 'print' function which is actually injected by said wrapper. For test purposes you can just replace 'print' with 'java.lang.System.out.print'.

int optimisationLevel = 3;

int languageVersion = Context.VERSION_1_7;

try {

Context cx = Context.enter();

cx.setOptimizationLevel(optimisationLevel);

cx.setLanguageVersion(languageVersion);

ImporterTopLevel scope = new ImporterTopLevel(cx);

cx.evaluateString(scope, TEST_SCRIPT1, "doTest", 1, null);

for (int i = 0; i < 10; i++)

cx.evaluateString(scope, "doTest();", "", 1, null);

} finally {

Context.exit();

}

You mentioned the following:

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.

I'd be interested in the source that reported this, My fibonacci function in java takes about 1/30 the time as the compiled js implementation. But perhaps I'm missing something.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值