Re: [Rd] Best practices for writing R functions (really copying)

From: Henrik Bengtsson <hb_at_biostat.ucsf.edu>
Date: Mon, 25 Jul 2011 11:11:59 -0700

Use tracemem() instead, i.e.

> A <- matrix(c(1.0,1.1), nrow=5, ncol=10);
> tracemem(A);

[1] "<0x00000000047ab170"
> A[1,1] <- 7;
> B <- sqrt(A);

tracemem[0x00000000047ab170 -> 0x000000000552f338]:
> A[1,1] <- 7;
> B <- t(A);
> A[1,1] <- 7;

tracemem[0x00000000047ab170 -> 0x00000000057ba588]:
> A[1,1] <- 7;
> A[1,1] <- 7;

It looks like sqrt() creates the copy internally, which explains the difference.

However, it is true that even if a new copy is not needed/created inside a function call, a function "touching" the object would trigger downstream copies, e.g.

# Not touching the object:
> foo <- function(X) { 0 }
> B <- foo(A);
> A[1,1] <- 7;
> A[1,1] <- 7;

# Touching the object:
> bar <- function(X) { Y <- X; 0 }
> B <- bar(A);
> A[1,1] <- 7;

tracemem[0x00000000039b5538 -> 0x000000000402c448]:
> A[1,1] <- 7;

However however, try doing the same with a vector instead of matrix, e.g. A <- 1:10, and/or assignment with A[1] <- 7 and you get a different behavior. The source code should explain why. I leave it at this.

My $.02

/Henrik

On Mon, Jul 25, 2011 at 8:53 AM, Radford Neal <radford_at_cs.toronto.edu> wrote:
> Gabriel Becker writes:
>
>  AFAIK R does not automatically copy function arguments. R actually tries
>  very hard to avoid copying while maintaining "pass by value" functionality.
>
>  ... R only copies data when you modify an object, not
>  when you simply pass it to a function.
>
> This is a bit misleading.  R tries to avoid copying by maintaining a
> count of how many references there are to an object, so that x[i] <- 9
> can be done without a copy if x is the only reference to the vector.
> However, it never decrements such counts.  As a result, simply passing
> x to a function that accesses but does not change it will result in x
> being copied if x[i] is changed after that function returns.  An
> exception is that this usually isn't the case if x is passed to a
> primitive function.  But note that not all standard functions are
> technically "primitive".
>
> The end result is that it's rather difficult to tell when copying will
> be done.  Try the following test, for example:
>
>  cat("a: "); print(system.time( { A <- matrix(c(1.0,1.1),50000,1000); 0 } ))
>  cat("b: "); print(system.time( { A[1,1]<-7; 0 } ))
>  cat("c: "); print(system.time( { B <- sqrt(A); 0 } ))
>  cat("d: "); print(system.time( { A[1,1]<-7; 0 } ))
>  cat("e: "); print(system.time( { B <- t(A); 0 } ))
>  cat("f: "); print(system.time( { A[1,1]<-7; 0 } ))
>  cat("g: "); print(system.time( { A[1,1]<-7; 0 } ))
>
> You'll find that the time printed after b:, d:, and g: is near zero,
> but that there is non-negligible time for f:.  This is because sqrt
> is primitive but t is not, so the modification to A after the call
> t(A) requires that a copy be made.
>
>   Radford Neal
>
> ______________________________________________
> R-devel_at_r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>



R-devel_at_r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel Received on Mon 25 Jul 2011 - 18:14:43 GMT

This quarter's messages: by month, or sorted: [ by date ] [ by thread ] [ by subject ] [ by author ]

All messages

Archive maintained by Robert King, hosted by the discipline of statistics at the University of Newcastle, Australia.
Archive generated by hypermail 2.2.0, at Tue 26 Jul 2011 - 08:40:13 GMT.

Mailing list information is available at https://stat.ethz.ch/mailman/listinfo/r-devel. Please read the posting guide before posting to the list.

list of date sections of archive