java7 / Rhino 中编译与解释 javascript 的性能

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14538541/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 21:54:21  来源:igfitidea点击:

Performance of the compiled vs. interpreted javascript in java7 / Rhino

javascriptjava-7rhino

提问by Xtra Coder

I have a problem with performance of Rhino javascript engine in Java7, shortly - my script (that parses and compiles texts) runs in Chrome around 50-100 times quicker than the same in Java7 Rhino script engine.

我很快就遇到了 Java7 中 Rhino javascript 引擎的性能问题 - 我的脚本(解析和编译文本)在 Chrome 中的运行速度比 Java7 Rhino 脚本引擎中的脚本快 50-100 倍。

I was trying to find the way to improve situation and have found that Rhino supports compilation of scripts. I tried doing it with my scripts and actually did not see any improvement. Finally - i ended up with a dummy short test suite where i do not see any difference in performance between compiled and interpreted versions. Please let me know what I'd doing wrong.

我试图找到改善情况的方法,发现 Rhino 支持脚本编译。我试着用我的脚本来做,但实际上没有看到任何改进。最后 - 我最终得到了一个虚拟的简短测试套件,我没有看到编译版本和解释版本之间的性能差异。请让我知道我做错了什么。

Note: some sources mention that Rhino engine runs compiled script roughly 1.6 slower than the "same" code written directly in Java. Not sure if "compilation of script" used in this sample the is same one which is supposed there.

注意:一些消息来源提到 Rhino 引擎运行编译后的脚本比直接用 Java 编写的“相同”代码慢大约 1.6 倍。不确定此示例中使用的“脚本编译”是否与此处假设的相同。

Test java class is below and sample result I'm getting from it on my machine ...

测试 java 类在下面,我在我的机器上从它得到了示例结果......

Results

结果

     Running via com.sun.script.javascript.RhinoScriptEngine@c50443 ... 
      time: 886ms, chars: 38890, sum: 2046720
      time: 760ms, chars: 38890, sum: 2046720
      time: 725ms, chars: 38890, sum: 2046720
      time: 765ms, chars: 38890, sum: 2046720
      time: 742ms, chars: 38890, sum: 2046720
       ... 3918ms


     Running via com.sun.script.javascript.RhinoCompiledScript@b5c292 @ com.sun.script.javascript.RhinoScriptEngine@f92ab0 ... 
      time: 813ms, chars: 38890, sum: 2046720
      time: 805ms, chars: 38890, sum: 2046720
      time: 812ms, chars: 38890, sum: 2046720
      time: 834ms, chars: 38890, sum: 2046720
      time: 807ms, chars: 38890, sum: 2046720
       ... 4101ms

Updateafter comment from Anon-Micro:

来自 Anon-Micro 的评论后更新

After wrapping invocation of the JavaScript eval() and compile() in test class into ...

在将测试类中对 JavaScript eval() 和 compile() 的调用包装到 ...

import sun.org.mozilla.javascript.internal.Context;
try {
    Context cx = Context.enter();

    cx.setOptimizationLevel(9);
    cx.setLanguageVersion(170);

    ...
}
finally {
    Context.exit();
}

result changed signigicantly - from average 1.8 (in new version of test class) sec to ~150msec. However instance of the doTest() function extracted from ScriptEngine loaded via (CompiledScript = Compilable.compile()).eval(Bindings) -> Bindings.get("doTest")still says it is sun.org.mozilla.javascript.internal.InterpretedFunctionand its performance is slightly worse (around 10%) than version of JS loaded from pre-compiled bytecode (by Rhino 1.7r4) - so i'm still not sure what is actually happening behind the scene.

结果发生了显着变化 - 从平均 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.org.mozilla.javascript.internal.Context within internal sun's package looks to be a weird design for me - 'internal' denotes this class is assumed not to be used by developers and therefor there is not 'certified' way to manipulate optimization level of JS evaluator in Java 7.

PS:内部 sun 包中的 sun.org.mozilla.javascript.internal.Context 对我来说似乎是一个奇怪的设计 - “internal”表示假定开发人员不使用此类,因此没有“认证”的方式在 Java 7 中操作 JS 评估器的优化级别。

Test class (updated, doTestCompiled is loaded from external *.class)

测试类(更新,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();
            }
        }
    }
}

采纳答案by Anon-Micro

The Rhino engine is actually capable of compiling scripts into bytecode 'in-process', so you don't need to run the tool to generate .class files first. You only need to set 'optimisation level' and the engine will automatically pre-compile the script before executing it. One way to override the optimisation level is with the VM argument -Drhino.opt.level. Set this to anything between 0 and 9 and run your original test program and you should see better performance.

Rhino 引擎实际上能够将脚本编译为“进程内”字节码,因此您无需先运行该工具来生成 .class 文件。您只需要设置“优化级别”,引擎就会在执行之前自动预编译脚本。覆盖优化级别的一种方法是使用 VM 参数 -Drhino.opt.level。将此设置为 0 到 9 之间的任何值并运行您的原始测试程序,您应该会看到更好的性能。

This is the same optimisation setting used by the compiling tool you mentioned, by the way. https://developer.mozilla.org/en-US/docs/Rhino/Optimization

顺便说一下,这与您提到的编译工具使用的优化设置相同。https://developer.mozilla.org/en-US/docs/Rhino/Optimization

For total control of optimisation level and javascript version within your program, you can do the following. However you lose some of the trimmings that RhinoScriptEngine class (which is just a environment wrapper and not the javascript engine) provides. One such trimming is the 'print' function which is actually injected by said wrapper. For test purposes you can just replace 'print' with 'java.lang.System.out.print'.

要完全控制程序中的优化级别和 javascript 版本,您可以执行以下操作。但是,您会丢失 RhinoScriptEngine 类(它只是一个环境包装器,而不是 javascript 引擎)提供的一些修饰。一种这样的修剪是“打印”功能,它实际上是由所述包装器注入的。出于测试目的,您可以将“print”替换为“java.lang.System.out.print”。

    int optimisationLevel = 3;
    int languageVersion = Context.VERSION_1_7;

    try {
        Context cx = Context.enter();
        cx.setOptimizationLevel(optimisationLevel);
        cx.setLanguageVersion(languageVersion);

        ImporterTopLevel scope = new ImporterTopLevel(cx);
        cx.evaluateString(scope, TEST_SCRIPT1, "doTest", 1, null);

        for (int i = 0; i < 10; i++)
            cx.evaluateString(scope, "doTest();", "", 1, null);

    } finally {
        Context.exit();
    }

You mentioned the following:

你提到了以下几点:

Note: some sources mention that Rhino engine runs compiled script roughly 1.6 slower than the "same" code written directly in Java. Not sure if "compilation of script" used in this sample the is same one which is supposed there.

注意:一些消息来源提到 Rhino 引擎运行编译后的脚本比直接用 Java 编写的“相同”代码慢大约 1.6 倍。不确定此示例中使用的“脚本编译”是否与此处假设的相同。

I'd be interested in the source that reported this, My fibonacci function in java takes about 1/30 the time as the compiled js implementation. But perhaps I'm missing something.

我对报告这个的来源感兴趣,我在 java 中的斐波那契函数大约需要 1/30 的时间作为编译的 js 实现。但也许我错过了一些东西。

回答by Matthew Crumley

I've found that for simple programs at least, the extra time spent compiling your code can overshadow the time running it. Then don't forget, it takes a little while before HotSpot compiles the Java bytecode to native code.

我发现至少对于简单的程序来说,编译代码所花费的额外时间可能会掩盖运行它的时间。然后不要忘记,HotSpot 将 Java 字节码编译为本机代码需要一些时间。

I think if you used a longer-running benchmark with more complex code (as opposed to a relatively simple program that's doing a lot of library calls) the compiled version would eventually win out, but the performance still won't be comparable to V8.

我认为,如果您使用具有更复杂代码的更长时间运行的基准测试(而不是执行大量库调用的相对简单的程序),编译版本最终会胜出,但性能仍然无法与 V8 相提并论。

Oracle is working on a newer EcmaScript engine for Java 8 that should be faster, but it's going to be awhile before that's available.

Oracle 正在为 Java 8 开发一种更新的 EcmaScript 引擎,它应该会更快,但要过一段时间才能使用。

回答by Xtra Coder

It looks like i've found what is wrong - compilation used in 'my' code (actually taken from internet samples) has nothing to do with 'compiled'.

看起来我发现了什么是错误的 - “我的”代码中使用的编译(实际上取自互联网样本)与“已编译”无关。

Finally i've got to this link - https://developer.mozilla.org/en-US/docs/Rhino_JavaScript_Compiler- the Rhino's tool to compile .js into .class. I've got following result with the same JS code running compiled from .class's bytecode:

最后我得到了这个链接 - https://developer.mozilla.org/en-US/docs/Rhino_JavaScript_Compiler- Rhino 将 .js 编译成 .class 的工具。我使用从 .class 的字节码编译运行的相同 JS 代码得到以下结果:

 time: 202ms, chars: 38890, sum: 2046720
 time: 92ms, chars: 38890, sum: 2046720
 time: 73ms, chars: 38890, sum: 2046720
 ...
 time: 71ms, chars: 38890, sum: 2046720
 time: 66ms, chars: 38890, sum: 2046720
 time: 64ms, chars: 38890, sum: 2046720
  ... 1143ms (per 15 iterations)

--- sleep 5 secs ---

 time: 64ms, chars: 38890, sum: 2046720
 time: 52ms, chars: 38890, sum: 2046720
 time: 64ms, chars: 38890, sum: 2046720
 ...
 time: 62ms, chars: 38890, sum: 2046720
 time: 67ms, chars: 38890, sum: 2046720
 time: 67ms, chars: 38890, sum: 2046720
  ... 962ms


--- sleep 5 secs ---

 time: 66ms, chars: 38890, sum: 2046720
 time: 56ms, chars: 38890, sum: 2046720
 time: 59ms, chars: 38890, sum: 2046720
 ...
 time: 69ms, chars: 38890, sum: 2046720
 time: 67ms, chars: 38890, sum: 2046720
 time: 59ms, chars: 38890, sum: 2046720
  ... 966ms

(this is roughly 10 time quicker)

(这大约快 10 倍)

and this is the result from Chrome:

这是 Chrome 的结果:

 time: 5ms, chars: 38890, sum: 2046720
 time: 3ms, chars: 38890, sum: 2046720
 time: 1ms, chars: 38890, sum: 2046720
 time: 1ms, chars: 38890, sum: 2046720
 time: 1ms, chars: 38890, sum: 2046720
 time: 5ms, chars: 38890, sum: 2046720
 time: 0ms, chars: 38890, sum: 2046720
 time: 1ms, chars: 38890, sum: 2046720
 time: 1ms, chars: 38890, sum: 2046720
 time: 1ms, chars: 38890, sum: 2046720

(average is 3-4 msec, ~15 quicker than compiled Java/Rhino, and ~200 times quicker than interpreted Java/Rhino).

(平均为 3-4 毫秒,比编译的 Java/Rhino 快约 15 倍,比解释的 Java/Rhino 快约 200 倍)。