首页 > 解决方案 > 当外部程序通过 R 的 C API 接口调用“Rf_allocXXX”时,谁在管理内存?

问题描述

我正在用 Rust 编写一个 R 包,它通过其 C API 接口与 R 通信。

一个对我来说似乎很棘手的基本问题是内存管理。

所以首先让我简要解释一下我的 Rust 程序是如何与 R 通信的。


首先,在 R 端,它使用.Call(). 然后将 C 库链接到具有 C 兼容 ABI 的 Rust 静态库。

R 脚本正在做一些简单的工作,比如输入验证,并根据输入决定调用哪个 C 函数。然后 C 程序将调用传递给底层的 Rust 函数,这些函数作为 C 兼容函数公开。

到目前为止很清楚,一旦 Rust 完成计算并需要将结果发回,它就会变得棘手。


一种选择是直接调用Rf_allocXXXRust 端的函数,并将结果存储在其中。然后将 R 对象的原始指针传回 C,然后再传给 R。

但我不清楚这是否会导致内存泄漏

在我看来,如果 Rust 调用Rf_allocXXX,新的 R objected ( SEXP) 是在 Rust 程序的堆内存上创建的。当传递原始指针时,Rust 不会破坏对象。但是接下来会发生什么?

请注意,SEXPRust 以这种方式创建的 直接传递回 C 和 R。没有重新分配。所以我似乎很不清楚这是否SEXP会被 R 的 GC 正确释放。


一个相关的问题,

我从Rcpp的源代码中注意到的是,它似乎只是调用 R 的 C API 来创建它的各种向量,它们是SEXP.

但我还不清楚它如何处理内存管理。它只是简单地将SEXP对象转回并且 R 将正确处理 GC 吗?

标签: rrcpp

解决方案


这一切(一如既往)都在编写 R 扩展(而且始终不是最容易找到的......)

简而言之,当您调用 R 扩展包时,您基本上需要调用 R 的 API 及其Calloc()Free()例程(和变体)。为什么?因为您返回给 R 的任何东西都会成为 R 对象,并且与所有其他 R 对象无法区分并且行为相同。包括非常重要的垃圾收集。

而做到这一点的唯一方法是通过 R 自己的分配器。所以 Rcpp 使用它。并创建实际上无法区分的对象。这使得一切正常。

R> Rcpp::cppFunction("IntegerVector foo() { 
+                           IntegerVector v = {1, 2, 3}; return v; }") 
R> foo()
[1] 1 2 3 
R> identical(foo(), c(1L, 2L, 3L))  
[1] TRUE  
R> identical(foo(), 1:3)    
[1] TRUE    
R> 

但是作为第一步,你可以在 Rust 中做你自己的事情来计算结果,然后支付一次性转换成本(从你的对象到SEXPR 期望作为 a 的结果.Call())。“在你跑之前先走”等等。Rust 绑定会很酷。我想你知道 Jeroen 和其他人已经做了一些工作吧?


推荐阅读