Rhino性能和编译脚本

时间:2014-05-31 22:26:52

标签: javascript rhino

我遇到了一些性能问题,因为在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>

从这些结果来看,这些是我得出的结论:

  • 应在范围内运行库和实用程序方法,并进行所有优化。
  • 缓存作用域,这样您就不需要再次解析脚本了。
  • 当您运行一次性脚本时,请不要使用优化。
  • 不要使用ScriptEngine接口,因为它似乎比使用Rhino慢,即使使用编译过的脚本也是如此。

所以这就是我的问题:有没有办法以更有效的方式运行Rhino脚本?

我的脚本不会经常更改(但是更改并需要在不停止应用程序的情况下更新它们),因此我可以编译它们并重用它们。但是我不确定我在做什么(缓存和重用范围)是最有效的方法。我已经看到有些人建议将Javascript编译为Java字节码,但不确定如何做到这一点。

注意:

1 个答案:

答案 0 :(得分:1)

Rhino引擎实现了Compilable接口。我认为这将比不断调整脚本字符串更快。

Compilable compilingEngine = (Compilable) cEngine; 
CompiledScript compiledScript = compilingEngine.compile(script);