当前位置: 首页 > 知识库问答 >
问题:

如何重用ScriptContext(或以其他方式提高性能)?

潘向明
2023-03-14

我有一个自制的ETL解决方案。转换层在JavaScript脚本的配置文件中定义,由Java的Nashorn引擎解释。

我遇到了性能问题。也许没有什么可以做的,但我希望有人能找到我使用Nashorn的方式有帮助的问题。该过程是多线程的。

我创建了一个静态脚本引擎,它只用于创建CompiledScript对象。

private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");

我将在每条记录上重新执行的Scriptlet编译成CompiledScript对象。

public static CompiledScript compile(Reader reader) throws ScriptException {
    return ((Compilable) engine).compile(reader);
}

有两个标准JavaScript库也使用这种方法编译。

对于每条记录,都会创建一个ScriptContext,添加标准库,并将记录的值设置为绑定。

public static ScriptContext getContext(List<CompiledScript> libs, Map<String, ? extends Object> variables) throws ScriptException {    
    SimpleScriptContext context = new SimpleScriptContext();
    Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);

    for (CompiledScript lib : libs) {
        lib.eval(context);
    }

    for (Entry<String, ? extends Object> variable : variables.entrySet()) {
        bindings.put("$" + variable.getKey(), variable.getValue());
    }
    return context;
}

然后使用记录的上下文转换记录并计算过滤器,所有这些都使用编译脚本。

public static String evalToString(CompiledScript script, ScriptContext context) throws ScriptException {
    return script.eval(context).toString();
}

针对ScriptContext的已编译脚本的实际执行速度非常快,但是ScriptContext的初始化速度非常慢。不幸的是,至少据我所知,这必须按一组绑定进行。如果记录与筛选器匹配,那么我必须再次为同一记录重建上下文,这一次使用匹配筛选器中的一些附加绑定。

每当我创建ScriptContext时,必须重新执行这两个标准库似乎效率很低,但是在执行这些库之后但添加绑定之前,我没有找到线程安全的方法来克隆ScriptContext。如果记录与过滤器匹配,则必须重新执行两个标准库并重新附加记录中的所有绑定,这似乎效率很低,但我仍然没有找到线程安全的方法来克隆记录的ScriptContext,以便在不改变原始绑定的情况下向其附加另一个绑定。

根据jVisual alvm,我的程序的大部分时间都花在

jdk.internal.dynalink.support.AbstractRelinkableCallSite.initialize() (70%)
jdk.internal.dynalink.ChainedCallSite.relinkInternal() (14%)

我将感谢任何对Nashorn的洞察,这有助于提高这个用例的性能。谢谢你。

共有1个答案

葛海阳
2023-03-14

我成功地使用了ThreadLocal来避免相声。它运行了1000000个测试来观察串扰,但没有发现任何串扰。这一变化意味着我创建了4个ScriptContext对象,而不是大约8000000个。

package com.foo;

import java.util.UUID;
import java.util.stream.Stream;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

public class Bar {

    private static ScriptEngine engine;
    private static CompiledScript lib;
    private static CompiledScript script;

    // Use ThreadLocal context to avoid cross-talk
    private static ThreadLocal<ScriptContext> context;

    static {
        try {
            engine = new ScriptEngineManager().getEngineByName("JavaScript");
            lib = ((Compilable) engine)
                    .compile("var firstChar = function(value) {return value.charAt(0);};");
            script = ((Compilable) engine).compile("firstChar(myVar)");
            context = ThreadLocal.withInitial(() -> initContext(lib));
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }

    // A function to initialize a ScriptContext with a base library
    private static ScriptContext initContext(CompiledScript lib) {
        ScriptContext context = new SimpleScriptContext();
        try {
            lib.eval(context);
        } catch (ScriptException e) {
            e.printStackTrace();
        }
        return context;
    }

    // A function to set the variable binding, evaluate the script, and catch
    // the exception inside a lambda
    private static String runScript(CompiledScript script,
            ScriptContext context, String uuid) {
        Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
        bindings.put("myVar", uuid);
        String result = null;
        try {
            result = ((String) script.eval(context));
        } catch (ScriptException e) {
            e.printStackTrace();
        }
        return result;
    }

    // The driver function which generates a UUID, uses Nashorn to get the 1st
    // char, uses Java to get the 1st char, compares them and prints mismatches.
    // Theoretically if there was cross-talk, the variable binding might change
    // between the evaluation of the CompiledScript and the java charAt.
    public static void main(String[] args) {
        Stream.generate(UUID::randomUUID)
                .map(uuid -> uuid.toString())
                .limit(1000000)
                .parallel()
                .map(uuid -> runScript(script, context.get(), uuid)
                        + uuid.charAt(0))
                .filter(s -> !s.substring(0, 1).equals(s.substring(1, 2)))
                .forEach(System.out::println);
    }

}
 类似资料:
  • 问题内容: 为什么没有人提及嵌套循环联接? 问题答案: 如果希望/可以读取未提交的记录,则在联接上使用(NOLOCK)可能会提高性能。 什么时候应该使用“with(nolock)”

  • 问题内容: 我正在开发一个应用程序,当我从列表中选择一个值(文件)时,应该在另一种形式的jTextPane中打开它。我正在使用两个面板,一个是显示我的列表的mainpanel,另一个是ExcelSheet,当我单击列表值时,mainpanel关闭并显示新表单ExcelSheet,但不显示jTextPane中doc文件的内容。 问题答案: 使用封装,为了显示给定文件更新文本窗格中的代码。您可以从添加

  • 问题内容: 根据CSS文档: 特异性是由选择器中的属性和伪类的数量定义的。 因此,我的问题是,是否可以通过一遍又一遍地重复相同的类名来提高特异性? 例如: 将 具有更高的特异性 要么 ? 问题答案: 是的,有可能并且有意这样做。尽管CSS2规范中未提及,但Selectors3规范中明确提及: 注意: 允许同一简单选择器重复出现 [sic] ,并且确实增加了特异性。 因此,浏览器在遇到重复的简单选择

  • > 登录到https://developer.apple.com/account 然后转到这里https://developer.apple.com/download/more 复制了Xcode 8的下载链接,即http://adcdownload.apple.com/developer_tools/xcode_8/xcode_8.xip 我尝试但我只在终端上得到了以下内容: --2016-09-

  • 是一个装饰器函数,它将组件类的名称作为其输入,并在要绑定的包含组件的模板中找到其选择器。 也可以传递一个模板引用变量。 例如,我们将组件类Alert绑定到其选择器<my-alert>,并将其分配给属性alert。 这使我们能够访问类方法,如show()。 View Example 当模板中有多个嵌入式组件时,我们还可以使用。 它收集 Alert 组件的实例列表,存储在与数组类似的QueryList

  • 问题内容: 我通过遵循“自动化无聊的东西”来学习Python。该程序应该转到http://xkcd.com/并下载所有图像以供离线查看。 我使用的是2.7版和Mac。 由于某种原因,我遇到诸如“未提供模式”之类的错误,以及使用request.get()本身的错误。 这是我的代码: 错误如下: 问题是我已经多次阅读本书中有关程序的部分,阅读了“请求”文档,以及在此处查看其他问题。我的语法看起来正确。