【发布时间】:2014-05-31 22:26:52
【问题描述】:
我遇到了一些性能问题,因为在 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' 放在作用域中一次,然后在不同的上下文中,我在同一作用域内调用该函数,根本没有任何优化。
根据这些结果,我得出以下结论:
- 库和实用程序方法应在所有优化的范围内运行。
- 缓存范围,因此您无需再次解析脚本。
- 当您运行一次性脚本时,不要使用优化。
- 不要使用 ScriptEngine 界面,因为它似乎比使用 Rhino 慢,即使使用已编译的脚本也是如此。
所以我的问题是:有没有办法以更有效的方式运行 Rhino 脚本?
我的脚本不会经常更改(但确实会更改并且需要在不停止应用程序的情况下更新它们),因此我可以编译它们并重用它们。但是我不确定我在做什么(缓存和重用范围)是最有效的方法。我看到有些人建议将 Javascript 编译为 Java 字节码,但不确定如何实现。
注意事项:
- 我知道这不是进行微基准测试的正确方法,但结果非常一致,在这种情况下已经足够好。
- 我无法切换到 Java 8 并使用 Nashorn。
- 我确实读过Performance of the compiled vs. interpreted javascript in java7 / Rhino
【问题讨论】:
标签: javascript rhino