1

I'm writing a function where there is a binary operation running at the deepest level. Writing this function in Rcpp gave me a huge performance improvement for a fixed binary operation (+ for example), but when I try to find ways to generalize it for user input functions, I lose a lot of performance. Here's an example:

First, I'll define the same simple binary operation in R and Rcpp:

library(Rcpp)
library(benchmark)

cppFunction(
  code = "double simple_cpp(double a, double b) {
    double out = cos(a) + sin(b);
    return(out);
  }")

simple_r <- function(a,b) cos(a) + sin(b)

Then, a function that takes a function as argument and performs this operation many times

    cppFunction(
      code = "NumericVector many_simple_cpp(NumericVector a, NumericVector b, Function simple) {
      NumericVector out(a.size());
      for(int i = 0; i<a.size(); i++){
        out[i] = Rcpp::as<double>(simple(a[i],b[i]));
      }
      return(out);
    }")

And third, a function that uses a pre-defined C++ function as the operator

cppFunction(
  code = "
  double operation(double a, double b){
    return(cos(a) + sin(b));
  }

  NumericVector full_native_cpp(NumericVector a, NumericVector b) {
    NumericVector out(a.size());
    for(int i = 0; i<a.size(); i++){
      out[i] = operation(a[i],b[i]);
    }
    return(out);
  }")

Some performance metrics:

test <- 1:10000
benchmark(rfun = many_simple_cpp(test,test,simple_r),
          cppfun = many_simple_cpp(test,test,simple_cpp),
          cppnative = full_native_cpp(test,test))
       test replications elapsed relative user.self sys.self
2    cppfun          100   15.95    159.5     15.93     0.02
3 cppnative          100    0.10      1.0      0.09     0.00
1      rfun          100   14.71    147.1     14.67     0.00 

My question is: The third example made it clear that I can generalize the type of operation without losing too much performance, but my two approaches were incorrect. I think the performance loss was caused by calling Rcpp::as<double>(simple(a[i],b[i])); multiple times. Is there a way I can let the user create the C++ function to pass as argument (or convert an R function to C++)?

Why passing simple_cpp and simple_r gave me similar results? I expected calls with simple_cpp to have better performance because it was a function defined with Rcpp.

VFreguglia
  • 2,129
  • 4
  • 14
  • 35
  • 4
    Yes. Discussed a few times in a few places and used in at least the RcppDE package. – Dirk Eddelbuettel Dec 21 '18 at 18:04
  • 1
    @Joseph Wood: I would delete/remove that comment. That is a *wrong* example as it passes an function. OP wants to pass a C++ function around which we do in RcppDE (as I said), discuss [here on the RcppGallery](http://gallery.rcpp.org/articles/passing-cpp-function-pointers/) and also discussed a few times at SO, eg [here](https://stackoverflow.com/questions/14428687/rcpparmadillo-pass-user-defined-function) and possibly a few times since. – Dirk Eddelbuettel Dec 22 '18 at 20:36
  • @DirkEddelbuettel, isn’t the OP creating a function that is visible to R, passing it, and wondering about the performance hit? – Joseph Wood Dec 22 '18 at 20:44
  • Quoting from penultimate paragraph of OP: "Is there a way I can let the user create the C++ function to pass as argument (or convert an R function to C++)?" – Dirk Eddelbuettel Dec 22 '18 at 20:48
  • 3
    The RcppGallery example on @DirkEddelbuettel 's comment is exactly what I needed. – VFreguglia Dec 22 '18 at 20:56
  • @DirkEddelbuettel, I completely misunderstood. Should I still delete the comment in question. – Joseph Wood Dec 22 '18 at 21:04
  • 1
    No worries. You have been helpful in many other questions. And as the OP has now been helped.... – Dirk Eddelbuettel Dec 22 '18 at 21:06

0 Answers0