gpt4 book ai didi

java-8 - 纳肖恩效率低下

转载 作者:行者123 更新时间:2023-12-04 02:35:48 25 4
gpt4 key购买 nike

我正在使用 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);

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

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

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

最佳答案

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

将您的代码包装在立即调用的匿名函数表达式中

你可以通过在匿名函数中定义你的程序来提高性能,从而给它一个非全局范围:

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

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

通过将全局变量作为参数传递来减少对全局变量的依赖

一般来说,如果您的代码对性能敏感,请尽量减少对全局变量的使用。如果您需要使用 globals ,您甚至可以将它们移动到函数的参数中,以便它们成为那里的局部变量。例如。如果您的代码依赖于全局变量 xy , 做:
(function(x, y) {
// put your original code here, like this:
// ...
function someFunction() { ... }
...
someFunction();
...
})(x, y);

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

使用外部匿名函数来保存代码和另一个用于评估

事实上,你可以做得更好。在上面的示例中,您仍将评估 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();
...
}
})();

(此时,您甚至不必使用 Java 中的 CompiledScript,但我建议您在向引擎传达您想要为重复评估优化表示的意图时这样做)。

从 Java 中,现在您可以执行 script.eval()后跟 JSObject program = (JSObject)context.get("program")然后用 program.call(null, x, y) 多次调用它. ( JSObject 是 Nashorn 面向 Java 的接口(interface),用于原生对象,包括普通对象和函数)。

或者,您可以使用 engine.compile("program(x, y)" 创建不同的脚本。调用并确保输入 xy进入 eval() 之前的上下文ing它。

通过这种方式,您将最大程度地减少重复评估。不过需要注意的重要一点是,所有调用都将共享最外层匿名调用的词法范围。这就是你如何获得相同的函数对象而无需重新创建它们,但也要注意,如果你有任何可变状态(函数范围内的一些 var s)它们也将被共享。

关于java-8 - 纳肖恩效率低下,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27494130/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com