Rcpp 按引用传递与按值传递 [英] Rcpp pass by reference vs. by value

查看:40
本文介绍了Rcpp 按引用传递与按值传递的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我通过 inline 第一次尝试 Rcpp 函数,它解决了我的速度问题(感谢 Dirk!):用零替换负值

最初的版本是这样的:

库(内联)cpp_if_src <- 'Rcpp::NumericVector xa(a);int n_xa = xa.size();for(int i=0; i < n_xa; i++) {如果(xa[i]<0) xa[i] = 0;}返回 xa;'cpp_if <- cxxfunction(signature(a=numeric"), cpp_if_src, plugin=Rcpp")

但是当调用 cpp_if(p) 时,它用输出覆盖了 p,这不是预期的.所以我认为它是通过引用传递的.

所以我用以下版本修复了它:

库(内联)cpp_if_src <- 'Rcpp::NumericVector xa(a);int n_xa = xa.size();Rcpp::NumericVector xr(a);for(int i=0; i < n_xa; i++) {如果(xr[i]<0)xr[i] = 0;}返回 xr;'cpp_if <- cxxfunction(signature(a=numeric"), cpp_if_src, plugin=Rcpp")

这似乎有效.但是现在,当我将其重新加载到 R 中时,原始版本不再覆盖其输入(即,现在相同的代码不会覆盖其输入):

<代码>>cpp_if_src <- '+ Rcpp::NumericVector xa(a);+ int n_xa = xa.size();+ for(int i=0; i < n_xa; i++) {+ if(xa[i]<0) xa[i] = 0;+ }+ 返回 xa;+ '>cpp_if <- cxxfunction(signature(a=numeric"), cpp_if_src, plugin=Rcpp")>>磷[1] -5 -4 -3 -2 -1 0 1 2 3 4 5>cpp_if(p)[1] 0 0 0 0 0 0 1 2 3 4 5>磷[1] -5 -4 -3 -2 -1 0 1 2 3 4 5

我不是唯一一个试图复制这种行为并发现结果不一致的人:

https://chat.stackoverflow.com/transcript/message/4357344#4357344

这是怎么回事?

解决方案

他们的关键是代理模型"——您的 xa 确实与您的原始对象具有相同的内存位置,因此您最终会改变你的原件.

如果您不希望那样,您应该做一件事:使用 clone() 方法(深度)复制,或者可能显式创建一个新对象,将更改的对象写入其中.方法二不会这样做,您只需使用两个不同命名的变量,它们都是原始变量的指针"(在代理模型意义上).

但是,当您将 int 向量(来自 R)传递给 NumericVector 类型时,还有一个额外的复杂问题是隐式强制转换和复制:创建一个副本,然后原件不再被更改.

这是一个更明确的示例,类似于我在教程或研讨会中使用的示例:

库(内联)f1 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body='Rcpp::NumericVector xa(a);int n = xa.size();for(int i=0; i < n; i++) {如果(xa[i]<0) xa[i] = 0;}返回 xa;')f2 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body='Rcpp::NumericVector xa(a);int n = xa.size();Rcpp::NumericVector xr(a);//仍然指向一个for(int i=0; i < n; i++) {如果(xr[i]<0)xr[i] = 0;}返回 xr;')p <- seq(-2,2)打印(类(p))打印(cbind(f1(p),p))打印(cbind(f2(p),p))p <- as.numeric(seq(-2,2))打印(类(p))打印(cbind(f1(p),p))打印(cbind(f2(p),p))

这是我看到的:

edd@max:~/svn/rcpp/pkg$ r/tmp/ari.r加载所需的包:方法[1] 整数"磷[1,] 0 -2[2,] 0 -1[3,] 0 0[4,] 1 1[5,] 2 2磷[1,] 0 -2[2,] 0 -1[3,] 0 0[4,] 1 1[5,] 2 2[1]数字"磷[1,] 0 0[2,] 0 0[3,] 0 0[4,] 1 1[5,] 2 2磷[1,] 0 0[2,] 0 0[3,] 0 0[4,] 1 1[5,] 2 2edd@max:~/svn/rcpp/pkg$

因此,传递 int-to-float 还是 float-to-float 真的很重要.

I made a first stab at an Rcpp function via inline and it solved my speed problem (thanks Dirk!): Replace negative values by zero

The initial version looked like this:

library(inline)
cpp_if_src <- '
  Rcpp::NumericVector xa(a);
  int n_xa = xa.size();
  for(int i=0; i < n_xa; i++) {
    if(xa[i]<0) xa[i] = 0;
  }
  return xa;
'
cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp")

But when called cpp_if(p), it overwrote p with the output, which was not as intended. So I assumed it was passing by reference.

So I fixed it with the following version:

library(inline)
cpp_if_src <- '
  Rcpp::NumericVector xa(a);
  int n_xa = xa.size();
  Rcpp::NumericVector xr(a);
  for(int i=0; i < n_xa; i++) {
    if(xr[i]<0) xr[i] = 0;
  }
  return xr;
'
cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp")

Which seemed to work. But now the original version doesn't overwrite its input anymore when I re-load it into R (i.e. the same exact code now doesn't overwrite its input):

> cpp_if_src <- '
+   Rcpp::NumericVector xa(a);
+   int n_xa = xa.size();
+   for(int i=0; i < n_xa; i++) {
+     if(xa[i]<0) xa[i] = 0;
+   }
+   return xa;
+ '
> cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp")
> 
> p
 [1] -5 -4 -3 -2 -1  0  1  2  3  4  5
> cpp_if(p)
 [1] 0 0 0 0 0 0 1 2 3 4 5
> p
 [1] -5 -4 -3 -2 -1  0  1  2  3  4  5

I'm not the only one who has tried to replicate this behavior and found inconsistent results:

https://chat.stackoverflow.com/transcript/message/4357344#4357344

What's going on here?

解决方案

They key is 'proxy model' -- your xa really is the same memory location as your original object so you end up changing your original.

If you don't want that, you should do one thing: (deep) copy using the clone() method, or maybe explicit creation of a new object into which the altered object gets written. Method two does not do that, you simply use two differently named variables which are both "pointers" (in the proxy model sense) to the original variable.

An additional complication, though, is in implicit cast and copy when you pass an int vector (from R) to a NumericVector type: that creates a copy, and then the original no longer gets altered.

Here is a more explicit example, similar to one I use in the tutorials or workshops:

library(inline)
f1 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body='
  Rcpp::NumericVector xa(a);
  int n = xa.size();
  for(int i=0; i < n; i++) {
    if(xa[i]<0) xa[i] = 0;
  }
  return xa;
')

f2 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body='
  Rcpp::NumericVector xa(a);
  int n = xa.size();
  Rcpp::NumericVector xr(a);            // still points to a
  for(int i=0; i < n; i++) {
    if(xr[i]<0) xr[i] = 0;
  }
  return xr;
')

p <- seq(-2,2)
print(class(p))
print(cbind(f1(p), p))
print(cbind(f2(p), p))
p <- as.numeric(seq(-2,2))
print(class(p))
print(cbind(f1(p), p))
print(cbind(f2(p), p))

and this is what I see:

edd@max:~/svn/rcpp/pkg$ r /tmp/ari.r
Loading required package: methods
[1] "integer"
        p
[1,] 0 -2
[2,] 0 -1
[3,] 0  0
[4,] 1  1
[5,] 2  2
        p
[1,] 0 -2
[2,] 0 -1
[3,] 0  0
[4,] 1  1
[5,] 2  2
[1] "numeric"
       p
[1,] 0 0
[2,] 0 0
[3,] 0 0
[4,] 1 1
[5,] 2 2
       p
[1,] 0 0
[2,] 0 0
[3,] 0 0
[4,] 1 1
[5,] 2 2
edd@max:~/svn/rcpp/pkg$

So it really matters whether you pass int-to-float or float-to-float.

这篇关于Rcpp 按引用传递与按值传递的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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