我遇到了一些性能问题,因为在Rhino中执行Javascript代码很慢。
我编写了以下测试,以了解性能如何随着执行脚本的不同方式而改变:
public class SimpleScriptsPerformanceTest {
private static int TIMES = 10000;
// no caching scope; without optimizations
@Test
public void testRhino1() {
Context ctx = Context.enter();
try {
ctx.setLanguageVersion(170);
ScriptableObject scriptScope = ctx.initStandardObjects();
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
ctx.evaluateString(scriptScope, getScript1(), "script.js", 1, null);
}
long endTime = System.currentTimeMillis();
System.out.println("Total execution time (rhino1): " + (endTime-startTime) + "ms");
} catch (Exception e) {
e.printStackTrace();
} finally {
Context.exit();
}
}
// no caching scope; with optimizations
@Test
public void testRhino2() {
Context ctx = Context.enter();
try {
ctx.setOptimizationLevel(9);
ctx.setLanguageVersion(170);
ScriptableObject scriptScope = ctx.initStandardObjects();
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
ctx.evaluateString(scriptScope, getScript1(), "script.js", 1, null);
}
long endTime = System.currentTimeMillis();
System.out.println("Total execution time (rhino2): " + (endTime-startTime) + "ms");
} catch (Exception e) {
e.printStackTrace();
} finally {
Context.exit();
}
}
// caching scope; with optimizations for main function and for calling code
@Test
public void testRhino3() {
Context ctx = Context.enter();
try {
ctx.setOptimizationLevel(9);
ctx.setLanguageVersion(170);
ScriptableObject scriptScope = ctx.initStandardObjects();
ctx.evaluateString(scriptScope, getScript1(), "script.js", 1, null);
ctx.setOptimizationLevel(9);
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
ctx.evaluateString(scriptScope, getScript2(), "script.js", 1, null);
}
long endTime = System.currentTimeMillis();
System.out.println("Total execution time (rhino3): " + (endTime-startTime) + "ms");
} catch (Exception e) {
e.printStackTrace();
} finally {
Context.exit();
}
}
// caching scope; with optimizations for main function; no optimizations for calling code
@Test
public void testRhino4() {
Context ctx = Context.enter();
try {
ctx.setOptimizationLevel(9);
ctx.setLanguageVersion(170);
ScriptableObject scriptScope = ctx.initStandardObjects();
ctx.evaluateString(scriptScope, getScript1(), "script.js", 1, null);
ctx.setOptimizationLevel(-1);
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
ctx.evaluateString(scriptScope, getScript2(), "script.js", 1, null);
}
long endTime = System.currentTimeMillis();
System.out.println("Total execution time (rhino4): " + (endTime-startTime) + "ms");
} catch (Exception e) {
e.printStackTrace();
} finally {
Context.exit();
}
}
// caching scope; without optimizations; different contexts
@Test
public void testRhino5() {
ScriptableObject scriptScope = null;
Context ctx = Context.enter();
try {
ctx.setOptimizationLevel(9);
ctx.setLanguageVersion(170);
scriptScope = ctx.initStandardObjects();
ctx.evaluateString(scriptScope, getScript1(), "script.js", 1, null);
} catch (Exception e) {
e.printStackTrace();
} finally {
Context.exit();
}
ctx = Context.enter();
try {
ctx.setOptimizationLevel(-1);
ctx.setLanguageVersion(170);
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
ctx.evaluateString(scriptScope, getScript2(), "script.js", 1, null);
}
long endTime = System.currentTimeMillis();
System.out.println("Total execution time (rhino5): " + (endTime-startTime) + "ms");
} catch (Exception e) {
e.printStackTrace();
} finally {
Context.exit();
}
}
// script engine; eval
@Test
public void testScriptEngine1() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
engine.eval(getScript1());
}
long endTime = System.currentTimeMillis();
System.out.println("Total execution time (scriptEngine1): " + (endTime-startTime) + "ms");
}
// script engine; compiled script
@Test
public void testScriptEngine2() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
Compilable compilingEngine = (Compilable)engine;
CompiledScript script = compilingEngine.compile(getScript1());
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
script.eval();
}
long endTime = System.currentTimeMillis();
System.out.println("Total execution time (scriptEngine2): " + (endTime-startTime) + "ms");
}
private String getScript1() {
StringBuilder sb = new StringBuilder();
sb.append("var foo = function() {").append("\n");
sb.append(" var a = 5;").append("\n");
sb.append(" var b = 'test';").append("\n");
sb.append(" var c = 93.2;").append("\n");
sb.append(" var d = [];").append("\n");
sb.append(" for (var i = 0; i < 5; i++) {").append("\n");
sb.append(" if (i == 2) d.push('pepe');").append("\n");
sb.append(" else d.push('juan');").append("\n");
sb.append(" }").append("\n");
sb.append(" return res = a + b + c + d.join(',');").append("\n");
sb.append("}").append("\n");
sb.append("foo();").append("\n");
return sb.toString();
}
private String getScript2() {
StringBuilder sb = new StringBuilder();
sb.append("foo();").append("\n");
return sb.toString();
}
}
多次运行脚本后,我得到以下平均时间:
Total execution time (rhino1): 8120 ms
Total execution time (rhino2): 7946 ms
Total execution time (rhino3): 4350 ms
Total execution time (rhino4): 257 ms
Total execution time (rhino5): 188 ms
Total execution time (scriptEngine1): 1547 ms
Total execution time (scriptEngine2): 1090 ms
所以rhino5似乎是上面最有效的,其中将函数'foo'放在范围中一次,然后在不同的上下文中,我将该函数调用在同一范围内而根本没有优化。 / p>
从这些结果来看,这些是我得出的结论:
所以这就是我的问题:有没有办法以更有效的方式运行Rhino脚本?
我的脚本不会经常更改(但是更改并需要在不停止应用程序的情况下更新它们),因此我可以编译它们并重用它们。但是我不确定我在做什么(缓存和重用范围)是最有效的方法。我已经看到有些人建议将Javascript编译为Java字节码,但不确定如何做到这一点。
注意:
答案 0 :(得分:1)
Rhino引擎实现了Compilable接口。我认为这将比不断调整脚本字符串更快。
Compilable compilingEngine = (Compilable) cEngine;
CompiledScript compiledScript = compilingEngine.compile(script);