调用多语言函数时,GraalVM 是否使用相同的线程和堆 space?

Does GraalVM use same thread and heap space when calling polyglot functions?

如果我在 GraalVM 中从 Java 调用 R 代码(使用 GraalVM 的多语言功能),R 代码和 Java 代码 运行 是否在同一个 Java线程(即 OS 或 Java 线程等之间没有切换?)另外,它是否与“memory/heap” space 相同?也就是说,在下面的示例代码中(我取自 https://www.baeldung.com/java-r-integration

public double mean(int[] values) {
    Context polyglot = Context.newBuilder().allowAllAccess(true).build();
    String meanScriptContent = RUtils.getMeanScriptContent(); 
    polyglot.eval("R", meanScriptContent);
    Value rBindings = polyglot.getBindings("R");
    Value rInput = rBindings.getMember("c").execute(values);
    return rBindings.getMember("customMean").execute(rInput).asDouble();
}

调用 rBindings.getMember("c").execute(values) 是否导致值对象(整数数组)被复制?或者 GraalVM 是否足够聪明,可以将其视为指向同一内存的指针 space?如果它是一个副本,复制时间是否与正常的 java clone() 操作相同(或相似,例如在 20% 以内)?最后,调用多语言函数(在本例中是在 R 中实现的 customMean)是否与调用本机 Java 函数具有相同的开销?额外的问题:GraalVM JIT 编译器甚至可以跨层编译,例如我有这个:

final long sum = IntStream.range(0,10000)
.stream()
.map(x -> x+4)
.map(x -> <<<FastR version of the following inverse operation: x-4 >>>)
.sum();

GraalVM 编译器是否会像普通的 Java JIT 编译器一样聪明,并意识到上面的整个语句可以在没有两个映射操作的情况下简单地编写(因为它们相互抵消)?

仅供参考:我正在考虑使用 GraalVM 运行 我的 Java 代码和我的 R 代码,一旦我在这里发现的问题得到解决(Why is FASTR (ie GraalVM version of R) 10x *slower* compared to normal R despite Oracle's claim of 40x *faster*?)并且其中之一动机是我希望消除从 Java 调用 R(使用 RServe())的 50% 时间花费在网络 IO 上(因为 Java 通过 TCP/IP 与 RServer 通信,并且RServe 和 Java 在不同的线程和内存 spaces 等等)

does the R code and the Java code run on the same Java thread. Also, is it the same "memory/heap" space?

是的,是的。您甚至可以使用 GraalVM VisualVM 检查堆:它提供标准 Java 视图,您可以在其中看到 FastR 内部表示的实例,例如 RIntVector 与其他 Java 对象混合在一起,或者R 视图,您可以在其中查看整数向量、列表、环境...

does the call rBindings.getMember("c").execute(values) cause the values object (an array of ints) to be copied?

通常是:大多数对象都传递给 R as-is。在 R 中你有两个选择:

  • 明确地将它们转换为某种具体类型,即 as.integer(arg),它不会制作副本,但会明确告诉 R 您希望如何将该值视为“本机”R 类型,包括 R 的值语义.
  • 保留默认规则,一旦您的对象传递给某些 R 内置函数,就会应用这些规则,例如,int[] 被视为整数向量(但请注意,将其视为列表将是在某些情况下也是合理的)。这里也没有副本。并且对象本身保留其引用语义。

但是,有时FastR需要复制:

  • 一些内置函数还不能处理外来对象
  • R 语言经常隐式复制向量,因为它的值语义、参数强制等
  • 当一个向量被传递给原生 R 扩展时,我们需要将它的数据移动到堆外内存

我想说的是,如果你碰巧有一个非常大的向量,比如 GB 的数据,即使在常规 R 中,你也需要非常小心。注意:默认情况下,FastR 向量由 Java 数组,因此它们的大小限制也适用于 FastR 向量。

Finally, does calling a polyglot function (in this case customMean implemented in R) have the same overhead as calling a native Java function?

大部分是,除了无法将函数拉入并内联到周围的 Java 代码 (+) 中。调用本身与常规 Java 调用一样快。对于您给出的示例:它不能按照您的建议进行优化,因为 R 函数不能内联(+)。但是,我非常怀疑任何编译器都可以按照您的建议对其进行优化,即使这两个函数都是纯 Java 代码。话虽这么说,是的:编译器可以优化的一些事情,比如消除一些它可以很好分析的无用计算,是行不通的,因为不可能跨 Java <-> R 边界内联代码(+ ).

(+) 除非你用 Espresso 运行 Java 代码(Java on Truffle),否则你不会使用 Context API 但是Espresso 的互操作支持。