0

如果我在 GraalVM 中从 Java 调用 R 代码(使用 GraalVM 的 polyglot 函数),R 代码和 Java 代码是否在同一个 Java 线程上运行(即操作系统或 Java 线程之间没有切换等?)另外,它是否相同“内存/堆”空间?也就是说,在下面的示例代码中(我取自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 是否足够聪明,可以将其视为指向同一内存空间的指针?如果是副本,复制时间是否与正常的 java clone() 操作相同(或相似,即在 20% 以内)时间?最后,调用 polyglot 函数(在本例中为 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 代码,一旦我在这里确定的问题得到解决(为什么 FASTR(即 GraalVM 版本的 R)与普通 R 相比要慢 10 倍*尽管 Oracle 声称40x *faster*?),其中一个动机是我希望消除从 Java 调用 R(使用 RServe())花费在网络 IO 上的 50% 时间(因为 Java 通过 TCP/IP 和 RServe 与 RServer 通信和Java在不同的线程和内存空间等)

4

1 回答 1

1

R 代码和 Java 代码是否在同一个 Java 线程上运行。另外,它是相同的“内存/堆”空间吗?

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

调用 rBindings.getMember("c").execute(values) 会导致值对象(整数数组)被复制吗?

一般来说是的:大多数对象都按原样传递给 R。在 R 中,您有两种选择:

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

但是,有时 FastR 需要制作副本:

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

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

最后,调用 polyglot 函数(在本例中为 R 中实现的 customMean)是否与调用原生 Java 函数具有相同的开销?

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

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

于 2022-02-02T20:50:58.690 回答