我正在使用Nashorn实现一些对性能敏感的代码.我是这样做的:
private ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(new String[] { "--no-java" });
String someExpression = "someFunction() + someVariable";
// this compiled script gets cached, caching code omitted
CompiledScript script = ((Compilable)engine).compile(expr);
MyScriptContext context = new MyScriptContext();
Object output = script.eval(context);
Run Code Online (Sandbox Code Playgroud)
在运行时,Nashorn坚持要对MyScriptContext进行大量必要的调用.它坚持在每次调用eval()时调用MyScriptContext.getBindings().put("nashorn.global",anObject).然后它调用MyScriptContext.getAttribute("someVariable")(它应该)并调用MyScriptContext.getAttribute("someFunction")(它不应该).
它不应该调用"someFunction()",因为该函数在编译时可用."someFunction()"需要编译为字节码并在编译时绑定,而不是每次调用eval().eval()处于紧密循环中.
我如何说服Nashorn减少对MyScriptContext的调用?
Att*_*edi 34
Nashorn必须在上下文中查找每个全局定义的变量(包括全局定义的函数),因为全局变量可以在外部重新定义,并且无法知道它们没有被重新定义.因此,我们不能在字节码中提前绑定一个函数.我将概述几种改进性能的方法.
您可以通过在匿名函数中定义程序来提高性能,从而为其提供非全局范围:
(function() {
// put your original code here, like this:
// ...
function someFunction() { ... }
...
someFunction();
...
})();
Run Code Online (Sandbox Code Playgroud)
在这种情况下,匿名函数内的函数对象最终可能存储在字节码局部变量中.
通常,如果您的代码对性能敏感,请尽量减少对globals的使用.如果需要使用全局变量,甚至可以将它们移动到函数的参数中,以便它们成为局部变量.例如,如果你的代码依赖于全局x和y,这样做:
(function(x, y) {
// put your original code here, like this:
// ...
function someFunction() { ... }
...
someFunction();
...
})(x, y);
Run Code Online (Sandbox Code Playgroud)
显然,这仅适用于对变量的读访问.(这适用于任何函数,当然,不仅仅是一个匿名的立即调用的函数表达式;它只是我所需要的一个构造,当我需要的是从全局词汇上下文转移到私有的上下文时).
实际上,你可以做得更好.在上面的例子中,你仍然会评估anon函数的主体,它将创建函数对象.(这不是那么糟糕,请注意;它们不会再被编译.函数对象本质上是一对指针:一个用于编码,一个用于词法范围,并且可以快速创建.代码编译一次.)但是在你可以使你的anon函数的词法范围不可变,你只需创建一次并从中返回一个函数,它将在其自己的范围内看到所有其他函数:
var program = (function() {
// put all your function declarations and other constants here
...
function someFunction() { ... }
...
return new function(x, y) {
// put your original code, minus the function declarations etc. here
...
someFunction();
...
}
})();
Run Code Online (Sandbox Code Playgroud)
(此时,您甚至不必使用CompiledScriptJava,但我建议您在向发动机传达您希望为重复评估优化的表示的意图时执行此操作.
从Java开始,现在您可以script.eval()随后执行JSObject program = (JSObject)context.get("program")并随后多次调用它program.call(null, x, y).(JSObject是Nashorn面向Java的面向本机对象的接口,包括普通对象和函数).
或者,你可以创建使用不同的脚本engine.compile("program(x, y)"进行调用,并确保投入x和y进入上下文之前eval()荷兰国际集团它.
通过这种方式重复评估你会减少最多.但值得注意的是,所有调用都将共享最外层匿名调用的词法范围.这就是你如何获得相同的函数对象而不需要重新创建它们,但是要注意,如果你有任何可变状态(var函数范围中的一些)它们也将被共享.
| 归档时间: |
|
| 查看次数: |
2479 次 |
| 最近记录: |