Java7/Rhino中已编译与已解释JavaScript的性能 [英] Performance of the compiled vs. interpreted javascript in java7 / Rhino

查看:51
本文介绍了Java7/Rhino中已编译与已解释JavaScript的性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Java7中使用Rhino javascript引擎的性能存在问题,很快-我的脚本(用于解析和编译文本)在Chrome中运行的速度比Java7 Rhino脚本引擎中的运行速度快50-100倍.

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.

我试图找到改善情况的方法,并且发现Rhino支持脚本的编译.我尝试使用自己的脚本执行此操作,但实际上并没有看到任何改进.最后-我得到了一个虚拟的简短测试套件,在该套件中,我看不到编译版本和解释版本之间的性能差异.请让我知道我在做什么错.

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引擎运行的编译脚本比直接用Java编写的相同"代码慢大约1.6倍.不确定此示例中使用的脚本编译"是否应该与此处假定的相同.

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.

下面是测试Java类,示例结果是我在机器上从中得到的...

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

结果


     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

更新在Anon-Micro发表评论后:

Update after comment from Anon-Micro:

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

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

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

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

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

结果发生了显着变化-从平均1.8秒(在新版本的测试类中)更改为〜150毫秒.但是从通过(CompiledScript = Compilable.compile()).eval(Bindings) -> Bindings.get("doTest")加载的ScriptEngine中提取的doTest()函数的实例仍然说它是sun.org.mozilla.javascript.internal.InterpretedFunction,它的性能比从预编译的字节码加载的JS版本(由Rhino 1.7r4)稍差(大约10%). -所以我仍然不确定幕后到底发生了什么.

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.InterpretedFunction and 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.

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软件包中的sun.org.mozilla.javascript.internal.Context对我来说似乎是一个怪异的设计-内部"表示假定此类未被开发人员使用,因此未经过认证" Java 7中操作JS评估程序优化级别的方法.

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.

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

推荐答案

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

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.

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

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

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

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'.

    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();
    }

您提到了以下内容:

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

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.

我会对报道这一情况的消息源感兴趣,我的java中的fibonacci函数大约花费了已编译js实现的1/30的时间.但是也许我想念一些东西.

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.

这篇关于Java7/Rhino中已编译与已解释JavaScript的性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆