java 7 - java7 / Rhino中编译与解释JavaScript的性能
java 7 - Performance of the compiled vs. interpreted javascript in java7 / Rhino
我在Java7中的Rhino javascript引擎的性能有问题,很快 - 我的脚本(解析和编译文本)在Chrome中的运行速度比Java7 Rhino脚本引擎快50-100倍。
我试图找到改善情况的方法,发现 Rhino 支持脚本编译。我尝试使用我的脚本执行此操作,实际上没有看到任何改进。最后 - 我最终得到了一个虚拟的简短测试套件,我看不到编译版本和解释版本之间的性能有任何差异。请让我知道我做错了什么。
注意:一些来源提到 Rhino 引擎运行编译脚本比直接用 Java 编写的"相同"代码慢大约 1.6 个。不确定此示例中使用的"脚本编译"是否与那里应该使用的相同。
测试java类在下面,我在机器上从中获得的示例结果...
结果
通过com.sun.script.javascript.RhinoScriptEngine@c50443运行... 时间:886毫秒,字符:38890,总和:2046720 时间:760毫秒,字符:38890,总和:2046720 时间:725毫秒,字符:38890,总和:2046720 时间:765毫秒,字符:38890,总和:2046720 时间:742毫秒,字符:38890,总和:2046720 ...3918毫秒通过com.sun.script.javascript.RhinoCompiledScript@b5c292 @ com.sun.script.javascript.RhinoScriptEngine@f92ab0运行... 时间:813毫秒,字符:38890,总和:2046720 时间:805毫秒,字符:38890,总和:2046720 时间:812毫秒,字符:38890,总和:2046720 时间:834毫秒,字符:38890,总和:2046720 时间:807毫秒,字符:38890,总和:2046720 ...4101毫秒
Anon-Micro评论后的更新:
在将 JavaScript eval() 和 compile() 在测试类中的调用包装到...
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 版本略差(约 10%)(由 Rhino 1.7r4) - 所以我仍然不确定幕后到底发生了什么。
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 in inside sun的包对我来说似乎是一个奇怪的设计 - "internal"表示这个类被假定不被开发人员使用,因此没有"认证"的方式来操纵Java 7中JS评估器的优化级别。
测试类(更新,doTest编译从外部加载 *.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 之间的任何值并运行原始测试程序,您应该会看到更好的性能。
顺便说一下,这与你提到的编译工具使用的优化设置相同。 https://developer.mozilla.org/en-US/docs/Rhino/Optimization
要完全控制程序中的优化级别和 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();
}
你提到了以下内容:
注意:一些来源提到 Rhino 引擎运行编译脚本比直接用 Java 编写的"相同"代码慢大约 1.6 个。不确定此示例中使用的"脚本编译"是否与那里应该使用的相同。
我会对报告此消息的来源感兴趣,我在 java 中的斐波那契函数大约需要 1/30 的时间作为编译的 js 实现。但也许我错过了什么。
我发现至少对于简单的程序,编译代码所花费的额外时间可能会掩盖运行它的时间。然后不要忘记,HotSpot需要一段时间才能将Java字节码编译为本机代码。
我认为,如果您使用运行时间更长的基准测试以及更复杂的代码(而不是执行大量库调用的相对简单的程序),编译版本最终会胜出,但性能仍然无法与 V8 相提并论。
Oracle正在为Java 8开发更新的EcmaScript引擎,该引擎应该更快,但还需要一段时间才能可用。
看起来我发现了问题所在 - "我的"代码中使用的编译(实际上取自互联网样本)与"编译"无关。
最后,我得到了这个链接 - https://developer.mozilla.org/en-US/docs/Rhino_JavaScript_Compiler - 犀牛的工具,用于将.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
(这大约快了 10 倍)
这是来自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
(平均为 3-4 毫秒,比编译的 Java/Rhino 快 ~15 毫秒,比解释型 Java/Rhino 快 ~200 倍)。
- Appcelerator Titanium:在运行时下载并解释JavaScript代码
- 解释javascript中的+var和-var一元运算符
- 需要解释Javascript regex请求参数解析
- 解释 JavaScript 中数字的行为
- 请解释Javascript图像旋转代码
- 有人可以向我解释javascript getCookie()中while循环的功能吗?
- 有人可以帮助解释JavaScript中的这个正则表达式吗?
- Mozilla不能很好地解释JavaScript
- 任何人都可以解释JavaScript中的reduce函数吗?
- 解释JavaScript和php代码
- 一些帮助解释javascript函数
- 您将如何向初学者解释Javascript中的回调函数
- 有人能用一个真实的例子来解释JavaScript中函数和方法的区别吗
- 解释JavaScript中的变量作用域
- 解释JavaScript安全扫描警告
- 用Rhino在Java中解释JavaScript:暂停/恢复脚本
- 通过分解成更小的部分来解释javascript正则表达式
- 解释Javascript文件Laravel中的变量
- 需要解释javascript中的作用域问题
- 它“安全”吗?使用注释来解释Javascript中的函数