non c'è profonda copia in Rcpp a meno che non lo richieda con clone
. Quando passi di valore, stai creando un nuovo oggetto t utilizza lo stesso oggetto R sottostante.
Quindi il diverso è piccolo tra passaggio per valore e passaggio per riferimento.
Tuttavia, quando si passa per valore, è necessario pagare il prezzo per proteggere l'oggetto sottostante ancora una volta. Potrebbe incorrere in un costo aggiuntivo in quanto Rcpp si basa sul ricorsivo non molto efficiente R_PreserveObject
.
La mia linea guida dovrebbe passare per riferimento ogniqualvolta possibile, in modo da non pagare un prezzo di protezione extra. Se sai che ABCfun2
non cambierà l'oggetto, ti consiglio di passare per riferimento a const: ABCfun2(const List&)
. Se hai intenzione di apportare modifiche allo List
, allora ti consigliamo di utilizzare ABCfun2(List&)
.
Considerate questo codice:
#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) ;
}
Tutto quello che sto facendo è passare una lista a una funzione, aggiornare e stampare alcune informazioni circa la puntatore all'oggetto R sottostante e il puntatore all'oggetto List (this
).
ecco i risultati di ciò che accade quando chiamo test_copy
e 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
Si comincia con un elenco esistente associata a un oggetto R.
initial: SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0
passiamo per valore di fun_copy
modo da ottenere un nuovo List
ma utilizzando lo stesso oggetto R sottostante:
in fun_copy: SEXP=<0x7fda4926d278>. List=0x7fff5bb5ef30
Abbiamo uscita fun_copy
. stesso oggetto R sottostante di nuovo, e di nuovo al nostro originale List
:
after fun_copy (1): SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0
Ora noi chiamiamo di nuovo fun_copy
ma questa volta l'aggiornamento di un componente che non era sulla lista: x["d"]="foo"
.
in fun_copy: SEXP=<0x7fda48989120>. List=0x7fff5bb5ef20
List
aveva altra scelta che crearsi un nuovo oggetto R sottostante, ma questo oggetto è Sottostante solo al locale List
. Pertanto, quando usciamo da get_copy
, torniamo al nostro originale List
con il sottostante originale SEXP
.
after fun_copy (2): SEXP=<0x7fda4926d278>. List=0x7fff5bb5efd0
La cosa fondamentale è che la prima volta "a"
era già nella lista, quindi abbiamo aggiornate direttamente i dati. Poiché l'oggetto locale a fun_copy
e l'oggetto esterno da test_copy
condividono lo stesso oggetto R sottostante, le modifiche all'interno di fun_copy
sono state propagate.
La seconda volta, fun_copy
cresce l'oggetto locale List
, associandolo a un nuovo SEXP
che non si propaga alla funzione esterna.
Ora consideriamo cosa succede quando si passa da riferimento:
> 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"
C'è solo un oggetto List
0x7fff5b909fd0
. Quando dobbiamo ottenere un nuovo SEXP
nella seconda chiamata, viene propagato correttamente al livello esterno.
Per me, il comportamento che si ottiene quando si passa da riferimenti è molto più facile da ragionare.
Questo è molto chiaro per me ora! Grazie Romain e Dirk! Entrambi i tuoi consigli sono stati molto utili. Grande fan di Rcpp che cerca di mostrarlo al mondo di lingua spagnola http://rstudio-pubs-static.s3.amazonaws.com/16153_7d5a52078aa24b3da140e3f94e48226b.html#/ =) Grazie! – gvegayon
Buon post. Potresti aggiungere alcune misurazioni su un numero (abbastanza grande) di chiamate per vedere qual è il costo effettivo? –