在C ++函数中,Rcpp对象如何传递给其他函数(通过引用或复制)? [英] Within C++ functions, how are Rcpp objects passed to other functions (by reference or by copy)?

查看:126
本文介绍了在C ++函数中,Rcpp对象如何传递给其他函数(通过引用或复制)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚使用Rcpp编写了ABCoptim软件包的新版本.速度提高了大约30倍,我对新版本的性能(相对于旧版本)感到非常满意,但是对于是否有空间在不修改太多代码的情况下提高性能的问题,我仍然有些担忧.

I've just finished writing a new version of the ABCoptim package using Rcpp. With around 30x speed ups, I'm very happy with the new version's performance (vs old version), but I'm still having some concerns on if I have space to improve performance without modifying too much the code.

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

Within the main function of ABCoptim (written in C++) I'm passing around a Rcpp::List object containing "bees positions" (NumericMatrix) and some NumericVectors with important information for the algorithm itself. My question is, when I'm passing a Rcpp::List object around other functions, e.g.

#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的迭代副本可能会减慢我的代码的速度.

What does Rcpp does within the while loop? Does the x object is passed by reference or by deep copy to the functions ABCfun2 and ABCfun3? I've seen the usage of 'const List&x', which tells me that I can pass Rcpp objects using pointers, but the thing is that I need this list to be variable (and no constant), is there anyway to improve this? I'm afraid that iterative copy of this x List can be slowing down my code.

PS:我还是C ++的新手,此外,我正在使用Rcpp学习C ++.

PS: I'm still new to C++, furthermore I'm using Rcpp to learn C++.

推荐答案

除非您使用clone要求,否则Rcpp中没有深层副本.当按值传递时,您正在创建一个新的List对象,但它使用相同的基础R对象.

There is no deep copy in Rcpp unless you ask for it with clone. When you pass by value, you are making a new List object but it uses the same underlying R object.

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

So the different is small between pass by value and pass by reference.

但是,当您按值传递时,您必须再付出一次保护底层对象的代价.由于此Rcpp依赖于递归效率不是很高的R_PreserveObject,因此可能会产生额外的成本.

However, when you pass by value, you have to pay the price for protecting the underlying object one more time. It might incur extra cost as for this Rcpp relies on the recursive not very efficient R_PreserveObject.

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

My guideline would be to pass by reference whenever possible so that you don't pay extra protecting price. If you know that ABCfun2 won't change the object, I'd advise passing by reference to const : ABCfun2( const List& ). If you are going to make changes to the List, then I'd recommend using 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)的信息.

All I'm doing is pass a list to a function, update it and print some information about the pointer to the underlying R object and the pointer to the List object ( this ) .

这是我呼叫test_copytest_ref时发生的结果:

Here are the results of what happens when I call test_copy and 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对象关联的现有列表开始.

We start with an existing list associated with an R object.

           initial:  SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0

我们将其按值传递给fun_copy,因此我们得到了一个新的List,但使用了相同的基础R对象:

We pass it by value to fun_copy so we get a new List but using the same underlying R object:

       in fun_copy:  SEXP=<0x7fda4926d278>. List=0x7fff5bb5ef30

我们退出fun_copy.再次使用相同的基础R对象,并返回到原始的List:

We exit of fun_copy. same underlying R object again, and back to our original List :

after fun_copy (1):  SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0

现在,我们再次调用fun_copy,但是这次更新的是不在列表中的组件:x["d"]="foo".

Now we call again fun_copy but this time updating a component that was not on the list: x["d"]="foo".

       in fun_copy:  SEXP=<0x7fda48989120>. List=0x7fff5bb5ef20

List别无选择,只能为其自身创建一个新的基础R对象,但是该对象仅是局部List的基础.因此,当我们离开get_copy时,我们将返回其原始List及其原始基础SEXP.

List had no choice but to create itself a new underlying R object, but this object is only underlying to the local List. Therefore when we get out of get_copy, we are back to our original List with its original underlying SEXP.

after fun_copy (2):  SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0

这里的关键是第一次"a"已经在列表中,因此我们直接更新了数据.因为fun_copy的本地对象和test_copy的外部对象共享相同的基础R对象,所以传播了fun_copy内部的修改.

The key thing here is that the first time "a" was already on the list, so we updated the data directly. Because the local object to fun_copy and the outer object from test_copy share the same underlying R object, modifications inside fun_copy was propagated.

第二次,fun_copy增长了其本地List对象,并将其与一个不会传播到外部函数的全新SEXP关联.

The second time, fun_copy grows its local List object, associating it with a brand new SEXP which does not propagate to the outer function.

现在考虑通过引用传递时会发生什么:

Now consider what happens when you pass by reference :

> 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时,它会正确地传播到外部级别.

There is only one List object 0x7fff5b909fd0. When we have to get a new SEXP in the second call, it correctly gets propagated to the outer level.

对我来说,通过引用传递时得到的行为更容易推论.

To me, the behavior you get when passing by references is much easier to reason with.

这篇关于在C ++函数中,Rcpp对象如何传递给其他函数(通过引用或复制)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆