我很快遇到了 Java7 中 Rhino javascript 引擎的性能问题 - 我的脚本(解析和编译文本)在 Chrome 中的运行速度比 Java7 Rhino 脚本引擎中的脚本快 50-100 倍。
我试图找到改善情况的方法,发现 Rhino 支持脚本编译。我试着用我的脚本来做,实际上没有看到任何改进。最后 - 我最终得到了一个虚拟的简短测试套件,我没有看到编译版本和解释版本之间的性能差异。请让我知道我做错了什么。
注意:一些消息来源提到 Rhino 引擎运行编译脚本比直接用 Java 编写的“相同”代码慢大约 1.6。不确定此示例中使用的“脚本编译”是否与此处假定的相同。
下面是测试java类,我在我的机器上从中得到了示例结果......
结果
通过 com.sun.script.javascript.RhinoScriptEngine@c50443 运行 ... 时间:886ms,字符:38890,总和:2046720 时间:760ms,字符:38890,总和:2046720 时间:725ms,字符:38890,总和:2046720 时间:765ms,字符:38890,总和:2046720 时间:742ms,字符:38890,总和:2046720 ... 3918 毫秒 通过 com.sun.script.javascript.RhinoCompiledScript@b5c292 @ com.sun.script.javascript.RhinoScriptEngine@f92ab0 运行 ... 时间:813ms,字符:38890,总和:2046720 时间:805ms,字符:38890,总和:2046720 时间:812ms,字符:38890,总和:2046720 时间:834ms,字符:38890,总和:2046720 时间:807ms,字符:38890,总和:2046720 ... 4101 毫秒
Anon-Micro 发表评论后更新:
在将测试类中 JavaScript eval() 和 compile() 的调用包装到 ...
import sun.org.mozilla.javascript.internal.Context;
try {
Context cx = Context.enter();
cx.setOptimizationLevel(9);
cx.setLanguageVersion(170);
...
}
finally {
Context.exit();
}
结果发生了显着变化 - 从平均 1.8(在新版本的测试类中)秒到 ~150 毫秒。然而,从通过加载的 ScriptEngine 提取的 doTest() 函数的实例(CompiledScript = Compilable.compile()).eval(Bindings) -> Bindings.get("doTest")
仍然表示它是sun.org.mozilla.javascript.internal.InterpretedFunction
,并且它的性能比从预编译的字节码(由 Rhino 1.7r4)加载的 JS 版本稍差(大约 10%) - 所以我仍然不确定幕后实际发生了什么。
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 包中的 sun.org.mozilla.javascript.internal.Context 对我来说似乎是一个奇怪的设计 - 'internal' 表示这个类被假定不被开发人员使用,因此没有“认证”的方式来在 Java 7 中操作 JS 评估器的优化级别。
测试类(已更新,doTestCompiled 是从外部 *.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();
}
}
}
}