【问题标题】:Rhino performance and compiled scriptsRhino 性能和编译脚本
【发布时间】: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 字节码,但不确定如何实现。

注意事项:

【问题讨论】:

    标签: javascript rhino


    【解决方案1】:

    Rhino 引擎实现了 Compilable 接口。我认为这比必须一直评估脚本字符串要快。

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

    【讨论】:

    • 我认为 testScriptEngine2() 似乎不会产生最好的结果。
    • fwiw;在 Nashorn(java8) 中,engine.eval() 比compiledscript.eval() 慢得多。大约 10 秒后,compiledScript.eval() 会快得多。但在 Rhino 中,CompiledScript 的速度并没有变化。
    • 是的,可能,但我不能切换到 Java 8 以使用问题中所述的 Nashorn。无论如何,对于可以使用 Java 8 的人来说,这可能是一个很好的提示。
    • @dgaviola 您可能想用独立的 Rhino 尝试这个答案。独立的 Rhino 和内置引擎的性能特征可能不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-13
    • 1970-01-01
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多