Nashorn inefficiency

此生再无相见时 提交于 2019-12-02 18:32:36

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.

Wrap your code in an immediately invoked anonymous function expression

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.

Reduce dependence on globals by passing them in as parameters

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

Use outer anonymous function to hold the code and another one for evaluations

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

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

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

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.

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.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!