Here we explore passing objects efficiently between R and c++ (using Rcpp). This will likely be obvious to a computer scientist but…let’s learn by doing.

Firstly we have written two function in c++ that take a matrix and modify it by adding a constant. This is either done: 1. Across a row, using add_to_row() or 2. Down a column using add_to_col(). Rcpp passes by reference by default and therefore these functions modify in place (unless a copy of the matrix passed to them is forced to be made).

To show what these functions do:

x <- matrix(0L, nrow = 5, ncol = 5)
x
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    0    0    0    0    0
#> [2,]    0    0    0    0    0
#> [3,]    0    0    0    0    0
#> [4,]    0    0    0    0    0
#> [5,]    0    0    0    0    0

add_to_row(x, 0, 1)
x
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    1    1    1    1    1
#> [2,]    0    0    0    0    0
#> [3,]    0    0    0    0    0
#> [4,]    0    0    0    0    0
#> [5,]    0    0    0    0    0

add_to_col(x, 0, 1)
x
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    2    1    1    1    1
#> [2,]    1    0    0    0    0
#> [3,]    1    0    0    0    0
#> [4,]    1    0    0    0    0
#> [5,]    1    0    0    0    0

We can test the efficiency impact of modifying row-wise or column-wise:

test_row <- function(n){
  x <- matrix(0L, nrow = n, ncol = n)
  add_to_row(x, 0, 1)
}
test_col <- function(n){
  x <- matrix(0L, nrow = n, ncol = n)
  add_to_col(x, 0, 1)
}
row_v_col <- microbenchmark(test_row(1000), test_col(1000), times = 100)
ggplot2::autoplot(row_v_col)

We can see that, as the elements of the matrix are stored in the vector in memory in column-major order, operating column-wise is quicker. However the differences are quite small (~ 100 microseconds, or 1 ten thousandth of a second).

We need to be much more careful about making copies. We can show that is more inefficient if a copy is forced to be made if we pass a numeric matrix (not integer):

test_col_copy <- function(n){
  x <- matrix(0, nrow = n, ncol = n)
  add_to_col(x, 0, 1)
}
col_v_copy <- microbenchmark(test_col(1000), test_col_copy(1000), times = 100)
ggplot2::autoplot(col_v_copy)

Lets show how quickly we can do many steps between R and c++, modifying a large matrix at each step.

pass_back_and_forth <- function(x){
  out <- rep(0, 1000)
  for(i in 1:1000){
    add_to_col(x, 0, i)
    out[i] <- x[1,1]
  }
  return(out)
}
x <- matrix(0L, nrow = 1000, ncol = 1000)
out <- pass_back_and_forth(x)
head(out)
#> [1]  1  3  6 10 15 21
ggplot2::autoplot(microbenchmark(pass_back_and_forth(x), times = 10))