世卫组织';当外部程序通过R'调用'Rf#u allocXXX'时,管理内存;SCAPI接口?
我正在用Rust编写一个R包,它通过C API接口与R通信 对我来说,一个基本的问题似乎很棘手,那就是内存管理 首先让我简单地解释一下我的Rust程序是如何与R进行通信的世卫组织';当外部程序通过R'调用'Rf#u allocXXX'时,管理内存;SCAPI接口?,r,rcpp,R,Rcpp,我正在用Rust编写一个R包,它通过C API接口与R通信 对我来说,一个基本的问题似乎很棘手,那就是内存管理 首先让我简单地解释一下我的Rust程序是如何与R进行通信的 首先,在R端,它使用.Call()调用C动态库。然后,C库通过与C兼容的ABI链接到Rust静态库 R脚本正在做一些简单的工作,比如输入验证,并根据输入决定调用哪个C函数。然后C程序将调用传递给底层的Rust函数,这些函数作为C兼容函数公开 到目前为止,这是非常清楚的,一旦Rust完成计算并需要返回结果,它就会变得很棘手
首先,在R端,它使用
.Call()
调用C动态库。然后,C库通过与C兼容的ABI链接到Rust静态库
R脚本正在做一些简单的工作,比如输入验证,并根据输入决定调用哪个C函数。然后C程序将调用传递给底层的Rust函数,这些函数作为C兼容函数公开
到目前为止,这是非常清楚的,一旦Rust完成计算并需要返回结果,它就会变得很棘手
一个选项是直接调用Rust端的
Rf_allocXXX
函数,并在其中存储结果。然后将R对象的原始指针传递回C,然后传递回R
但我不清楚这是否会导致内存泄漏
在我看来,如果Rust调用Rf_allocXXX
,那么新的R对象(SEXP
)将在Rust程序的堆内存上创建。将原始指针传出时,锈迹不会破坏对象。但是接下来会发生什么呢
请注意,Rust以这种方式创建的SEXP
直接传递回C和R。没有重新分配。所以我似乎很不清楚这个SEXP
是否会被R的GC正确释放
相关问题, 我从
Rcpp
的源代码中注意到的是,它似乎只是调用R的C API来创建它的各种向量,这些向量是SEXP
的包装
但我还不清楚它是如何处理内存管理的。它是否只是简单地将SEXP
对象转回,R就能正确地处理GC?一切(一如既往)都在(而且总是不容易找到……)
简而言之,当您调用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中完成计算结果的工作,然后支付一次性转换成本(从对象到
SEXP
R期望的.Call()
)。“先走后跑”等等。铁锈装订会很酷。我想你知道Jeroen和其他人已经做了一些工作吗?更新此问题:
在阅读了《编写R扩展》一节之后,现在我意识到
- 如果一个可能使用其他语言的外国程序创建了一个
对象通过SEXP
接口或R\u allocXXX
中的等效项,然后 负责释放内存,Rcpp
- 在调用
、.C()
和.call()
.External()
- 或者当出现错误时
- 在调用
- 但是如果R对象是通过
或类似方式手动创建的 接口,用户负责释放内存,无论 此对象是在C、malloc
或其他外部程序中创建的Rcpp
R\u allocXXX
绑定来创建SEXP
,然后
将无内存泄漏。一旦物体转到R,它就安全了
但在处理一般的外部功能接口(FFI)时,仍需谨慎
- 如果我们手动分配内存并将一个C对象返回给C接口,那么
在转换C对象之前,我们需要确保FFI的任何一方都不会恐慌
至
。否则不会释放内存SEXP
- 同样,这也适用于当我们在C程序中手动分配内存时, 并将对象发送到外部程序
- 在任何情况下,手动分配的内存都需要手动释放
因此,一般来说,只要有可能,我们就应该使用
R\u allocXXX
接口来保护内存安全。是的,我找到了Jeroen的演示和。我当前的实现只是计算中间结果,然后将其作为通用C结构或基元类型发送给C包装函数,然后让C将这些C类型重新打包到SEXP
。但如果我能在Rust中直接创建SEXP
就更好了。我担心内存泄漏,因为一般来说,建议分配内存的人负责释放内存。例如,如果我在Rust中创建了一个向量,并将其指针和长度发送到C,那么通常我需要通过回调函数销毁该向量。但这一点并不清楚,因为R有自己的垃圾收集器。还有内存分析,请参阅(再次)编写R扩展。此外,CRAN非常勤奋,在所有软件包、SAN/UBSAN、analysis等方面运行valgrind。“不管人们怎么想”(基于第一眼),这不是一堆胡闹的业余代码。很可靠,对不起,我没说到你问题的那一部分。“当然”——在C++(有或没有RCPP)中,我们使用STL一整天,它有它的分配器。现代语言可以做到这一点:一旦超出范围,一切都会好起来。您不能将这些“本机”分配器用于返回到R的内容。通常情况下,值在超出范围时会被删除。实际上,如果一个值而不是它的引用被传递给另一个函数,