3 min read

C++ Rcpp11

Rcpp11 was released to CRAN, as the ultimate C++11 companion to R 3.1.2 on which it depends.

The NEWS extract follows:

# Rcpp11 3.1.2

* New `wrap` implementation for `std::tuple<Args...>` (#195)

* `colnames` and `rownames` setters for matrices (#210).

* Most sugar functions are now processing the expression in parallel.

* Forbidden symbols from the C/R API are no longer directly used,
so packages can use Rcpp11 without getting the NOTE as before.

This release marks departure from compromision. Rcpp11 used to compromise on the meaning of C++11 to satisfy platforms for which the available tooling was laging behind, most notoriously windows for which the C++ compiler currently shipped with Rtools only accomodates for some intermediate version called C++0x.

As of this version, Rcpp11 truly requires C++11 and this is a feature. Of course it means that for some time, users will have to install their own tool chain on windows. This is not expected to last as there has been whispers about tool chain changes for R 3.2.0.

This has been fixed by @kevinushey and even though we are still using the functions, we do it in a way that does no longer trigger the NOTEs.

Another important change that is a direct consequence of no longer compromising is that Rcpp11 now leverages hardware concurrency and some sugar functions now process the expression in parallel. This stack overflow thread has an example using the parallel sugar implementation of exp.

Consider this R/C++ script, compatible with Rcpp and Rcpp11:

#include <Rcpp.h>
using namespace Rcpp ;

// [[Rcpp::export]]
NumericVector exp2(NumericVector x) {  
   NumericVector z = Rcpp::clone(x);
   int n = z.size();
   for (int i=0; i<n; ++i)
      z[i] = exp(z[i]);
   return z;

// [[Rcpp::export]]
NumericVector expSugar(NumericVector x) {  
    return exp(x) ;

/*** R
    x <- rcauchy(1000000)
    microbenchmark(exp(x), exp2(x), expSugar(x))

Here are the result on my machine with the historical implementation of R/C++:

> microbenchmark(exp(x), exp2(x), expSugar(x))
Unit: milliseconds  
        expr      min       lq   median       uq      max neval
      exp(x) 7.027006 7.222141 7.421041 8.631589 21.78305   100
     exp2(x) 6.631870 6.790418 7.064199 8.145561 31.68552   100
 expSugar(x) 6.491868 6.761909 6.888111 8.154433 27.36302   100

and here are the results with Rcpp11:

> microbenchmark(exp(x), exp2(x), expSugar(x))
Unit: milliseconds  
        expr      min       lq   median       uq      max neval
      exp(x) 7.029882 7.077804 7.336214 7.656472 15.38953   100
     exp2(x) 6.636234 6.748058 6.917803 7.017314 12.09187   100
 expSugar(x) 1.652322 1.780998 1.962946 2.261093 12.91682   100

Now for a warning, Rcpp11 opts for a parallel version of sugar expressions. It is the responsability of the developper to protect against code that cannot run in parallel.

This can be done manually though mutexes, etc … but this is likely to lead to bad performance because of contention.

But this can also be done through the serial function. This function takes a sugar expression and returns another sugar expression that is guaranteed to execute serially, in one thread.

Rcpp11 makes no attempt at figuring out if the expression can run in parallel. This is the responsability of the developper of the code that uses it.