gpt4 book ai didi

r - 在 C++ 函数中,如何将 Rcpp 对象传递给其他函数(通过引用或复制)?

转载 作者:行者123 更新时间:2023-12-03 14:13:44 24 4
gpt4 key购买 nike

我刚刚使用 Rcpp 编写了新版本的 ABCoptim 包。有了大约 30 倍的加速,我对新版本的性能(与旧版本相比)非常满意,但我仍然担心是否有空间在不修改太多代码的情况下提高性能。

在 ABCoptim(用 C++ 编写)的主要功能中,我传递了一个 Rcpp::List 对象,其中包含“蜜蜂位置”(NumericMatrix)和一些带有算法本身重要信息的 NumericVectors。我的问题是,当我在其他函数周围传递一个 Rcpp::List 对象时,例如

#include <Rcpp.h>

using namespace Rcpp;

List ABCinit([some input]){[some code here]};
void ABCfun2(List x){[some code here]};
void ABCfun3(List x){[some code here]};

List ABCmain([some input])
{
List x = ABCinit([some input]);
while ([some statement])
{
ABCfun2(x);
ABCfun3(x);
}
...

return List::create(x["results"]);
}

Rcpp 在 while 循环中做了什么? x 对象是通过引用传递还是通过深拷贝传递给函数 ABCfun2ABCfun3 ?我已经看到了“const List&x”的用法,它告诉我可以使用指针传递 Rcpp 对象,但问题是我需要这个列表是可变的(而不是常量),有什么办法可以改进吗?我担心这个 x List 的迭代副本会减慢我的代码速度。

PS:我还是 C++ 新手,而且我正在使用 Rcpp 来学习 C++。

最佳答案

Rcpp 中没有深拷贝,除非你用 clone 请求它。 .当您按值传递时,您正在创建一个新的 List对象,但它使用相同的底层 R 对象。

因此,按值传递和按引用传递之间的差异很小。

但是,当你通过值(value)传递时,你必须为保护底层对象多付出一次代价。它可能会产生额外的成本,因为这个 Rcpp 依赖于递归而不是非常有效的 R_PreserveObject .

我的指导方针是尽可能通过引用传递,这样您就不会支付额外的保护价格。如果你知道 ABCfun2不会更改对象,我建议通过引用传递 const :ABCfun2( const List& ) .如果您要更改 List ,那么我建议使用 ABCfun2( List& ) .

考虑这段代码:

#include <Rcpp.h>
using namespace Rcpp ;

#define DBG(MSG,X) Rprintf("%20s SEXP=<%p>. List=%p\n", MSG, (SEXP)X, &X ) ;

void fun_copy( List x, const char* idx ){
x[idx] = "foo" ;
DBG( "in fun_copy: ", x) ;

}
void fun_ref( List& x, const char* idx ){
x[idx] = "bar" ;
DBG( "in fun_ref: ", x) ;
}


// [[Rcpp::export]]
void test_copy(){

// create a list of 3 components
List data = List::create( _["a"] = 1, _["b"] = 2 ) ;
DBG( "initial: ", data) ;

fun_copy( data, "a") ;
DBG( "\nafter fun_copy (1): ", data) ;

// alter the 1st component of ths list, passed by value
fun_copy( data, "d") ;
DBG( "\nafter fun_copy (2): ", data) ;


}

// [[Rcpp::export]]
void test_ref(){

// create a list of 3 components
List data = List::create( _["a"] = 1, _["b"] = 2 ) ;
DBG( "initial: ", data) ;

fun_ref( data, "a") ;
DBG( "\nafter fun_ref (1): ", data) ;

// alter the 1st component of ths list, passed by value
fun_ref( data, "d") ;
DBG( "\nafter fun_ref (2): ", data) ;


}

我所做的只是将一个列表传递给一个函数,更新它并打印一些关于指向底层 R 对象的指针和指向 List 对象的指针 ( this) 的信息。

以下是我调用 test_copy 时发生的结果和 test_ref :
> test_copy()
initial: SEXP=<0x7ff97c26c278>. List=0x7fff5b909fd0
in fun_copy: SEXP=<0x7ff97c26c278>. List=0x7fff5b909f30

after fun_copy (1): SEXP=<0x7ff97c26c278>. List=0x7fff5b909fd0
$a
[1] "foo"

$b
[1] 2

in fun_copy: SEXP=<0x7ff97b2b3ed8>. List=0x7fff5b909f20

after fun_copy (2): SEXP=<0x7ff97c26c278>. List=0x7fff5b909fd0
$a
[1] "foo"

$b
[1] 2

我们从与 R 对象关联的现有列表开始。
           initial:  SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0

我们将它按值传递给 fun_copy所以我们得到一个新的 List但使用相同的底层 R 对象:
       in fun_copy:  SEXP=<0x7fda4926d278>. List=0x7fff5bb5ef30

我们退出 fun_copy .再次使用相同的底层 R 对象,然后回到我们原来的 List :
after fun_copy (1):  SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0

现在我们再次调用 fun_copy但这次更新了一个不在列表中的组件: x["d"]="foo" .
       in fun_copy:  SEXP=<0x7fda48989120>. List=0x7fff5bb5ef20
List只好给自己创建一个新的底层R对象,但是这个对象只是底层到本地 List .因此,当我们离开 get_copy ,我们回到原来的 List以其原始基础 SEXP .
after fun_copy (2):  SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0

这里的关键是第一次 "a"已经在名单上,所以我们直接更新了数据。因为本地对象为 fun_copy和来自 test_copy 的外部对象共享相同的底层 R 对象,在 fun_copy 内部进行修改被传播。

第二次, fun_copy发展其本地 List对象,将其与全新的 SEXP 关联它不会传播到外部函数。

现在考虑通过引用传递时会发生什么:
> test_ref()
initial: SEXP=<0x7ff97c0e0f80>. List=0x7fff5b909fd0
in fun_ref: SEXP=<0x7ff97c0e0f80>. List=0x7fff5b909fd0

after fun_ref(1): SEXP=<0x7ff97c0e0f80>. List=0x7fff5b909fd0
$a
[1] "bar"

$b
[1] 2

in fun_ref: SEXP=<0x7ff97b5254c8>. List=0x7fff5b909fd0

after fun_ref(2): SEXP=<0x7ff97b5254c8>. List=0x7fff5b909fd0
$a
[1] "bar"

$b
[1] 2

$d
[1] "bar"

只有一个 List对象 0x7fff5b909fd0 .当我们必须得到一个新的 SEXP在第二次调用中,它正确地传播到外部级别。

对我来说,通过引用传递时得到的行为更容易理解。

关于r - 在 C++ 函数中,如何将 Rcpp 对象传递给其他函数(通过引用或复制)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24112893/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com