Nashorn效率低下 [英] Nashorn inefficiency

查看:467
本文介绍了Nashorn效率低下的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Nashorn实现一些对性能敏感的代码.我是这样的:

I'm implementing some performance-sensitive code using Nashorn. I do it like this:

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

在运行时,Nashorn坚持对MyScriptContext进行很多必要的调用.它坚持在每次调用eval()时都调用MyScriptContext.getBindings().put("nashorn.global",anObject).然后,它调用MyScriptContext.getAttribute("someVariable")(应调用),并调用MyScriptContext.getAttribute("someFunction")(应不调用).

At runtime, Nashorn insists on making a lot of necessary calls to MyScriptContext. It insists on calling MyScriptContext.getBindings().put("nashorn.global", anObject) on every call to eval(). It then makes a call to MyScriptContext.getAttribute("someVariable") (which it should) and a call to MyScriptContext.getAttribute("someFunction") (which it should not).

它不应调用"someFunction()",因为该函数在编译时可用. "someFunction()"需要编译为字节码并在编译时绑定,而不是在每次调用eval()时绑定. eval()处于紧密循环中.

It should not make the call for "someFunction()" because that function was available to it at compile time. "someFunction()" needs to get compiled to bytecode and bound in at compile time, not on every call to eval(). eval() is in a tight loop.

如何说服Nashorn减少对MyScriptContext的调用?

How do I persuade Nashorn to make fewer calls to MyScriptContext?

推荐答案

Nashorn必须在上下文中查找每个全局定义的变量(包括全局定义的函数),因为可以在外部重新定义全局变量,并且无法知道它们是否存在.没有重新定义.因此,我们永远无法将一个函数早期绑定到字节码中.我将概述提高性能的几种方法.

Nashorn has to look up every globally defined variable (including globally defined functions) in the context as the globals can be redefined externally and there's no way to know they aren't redefined. Therefore, we can't ever early-bind a function in bytecode. I'll outline several approaches for improving the performance.

您可以通过在匿名函数中定义程序来提高性能,从而为它提供非全局范围:

You could improve the performance by defining your program inside an anonymous function, thus giving it a non-global scope:

(function() {
    // put your original code here, like this:
    // ...
    function someFunction() { ... }
    ...
    someFunction();
    ...
})();

在这种情况下,匿名函数内部的函数对象最终可能存储在字节码局部变量中.

In that case, function objects inside the anonymous function can end up being stored in bytecode local variables.

通常,如果您的代码对性能敏感,请最小化其对全局变量的使用.如果需要使用全局变量,甚至可以将它们移到函数的参数中,以便它们在那里成为局部变量.例如.如果您的代码取决于全局变量xy,请执行以下操作:

In general, if your code is performance sensitive, minimize its use of globals. If you need to use globals , you can even move them into parameters of the function so they become local variables there. E.g. if your code depends on globals x and y, do:

(function(x, y) {
    // put your original code here, like this:
    // ...
    function someFunction() { ... }
    ...
    someFunction();
    ...
})(x, y);

很明显,这仅适用于对变量的读取访问. (当然,它可以用于任何函数,而不仅仅是立即调用的匿名函数表达式;当我需要的只是从全局词法上下文移到私有上下文时,这只是我使用的一种构造.)

Obviously, this only works for read-access to variables. (This works with any function, of course, not just an anonymous immediately invoked function expression; it's just a construct I use when all I need is move from global lexical context into a private one).

实际上,您可以做得更好.在上面的示例中,您仍将评估anon函数的主体,它将创建函数对象. (请注意,这还算不错,它们不会再次被编译.一个函数对象本质上是一对指针:一个指向代码,一个指向词法范围,并且创建起来很快.代码只编译一次.)但是在如果您可以使anon函数的词法范围不可变,则只需创建一次并从中返回一个函数,该函数将在其自己的范围内查看所有其他函数:

Actually, you can do even better. In the above example, you'll still evaluate the body of the anon function, and it will be creating function objects. (That's not so bad, mind you; they won't get compiled again. A function object is essentially a pair of pointers: one to code, one to the lexical scope and is fast to create. Code is compiled once.) But in case you can make your anon function's lexical scope immutable, you can just create it once and return a function from it that will see all the others in its own scope:

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

(在这一点上,您甚至不必使用Java中的CompiledScript,但是我建议您在向引擎传达想要优化表示以便重复评估的引擎意图时进行操作.)

(At this point, you don't even have to use CompiledScript from Java, but I suggest you do as you communicate your intent towards the engine that you want a representation optimized for repeated evaluation).

从Java开始,现在您可以先执行script.eval(),再执行JSObject program = (JSObject)context.get("program"),然后使用program.call(null, x, y)多次调用它. (JSObject是Nashorn的面向Java的本地对象(包括普通对象和函数)的接口).

From Java, now you can do script.eval() followed by JSObject program = (JSObject)context.get("program") and subsequently invoke it any number of times with program.call(null, x, y). (JSObject is Nashorn's Java-facing interface for native objects, both ordinary and functions).

或者,您可以使用engine.compile("program(x, y)"进行调用来创建其他脚本,并确保在eval()之前将xy放入上下文中.

Alternatively, you can create a different script using engine.compile("program(x, y)" for invocation and make sure to put x and y into the context before eval()ing it.

通过这种方式,您将减少重复评估的最多时间.需要注意的是,所有调用都将共享最外层匿名调用的词法范围.这样一来,您无需重新创建它们即可获得相同的函数对象,但也要注意,如果其中存在任何可变状态(函数范围内的某些var),它们也将被共享.

You'll cut down the most on repeated evaluation this way. What's important to notice though is that all invocations will share the lexical scope of the outermost anonymous invocation. That's how you get the same function objects without the need to ever recreate them, but also beware that if you have any mutable state in there (some vars in the function scope) they'll be shared too.

这篇关于Nashorn效率低下的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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