0

Consider this comparison:

require(Rcpp)
require(microbenchmark)

cppFunction('int cppFun (int x) {return x;}')
RFun = function(x) x

x=as.integer(2)
microbenchmark(RFun=RFun(x),cppFun=cppFun(x),times=1e5)

Unit: nanoseconds
   expr  min   lq      mean median   uq      max neval cld
   RFun  302  357  470.2047    449  513   110152 1e+06  a 
 cppFun 2330 2598 4045.0059   2729 2879 68752603 1e+06   b

cppFun seems slower than RFun. Why is it so? Do the times for calling the functions differ? Or is it the function itself that differ in running time? Is it the time for passing and returning arguments? Is there some data conversion or data copying I am unaware of when the data are passed to (or returned from) cppFun?

Remi.b
  • 17,389
  • 28
  • 87
  • 168
  • 11
    Foreign function interface has overhead, that should not be a surprise. When your function starts doing some actually useful work, you probably won't ever notice the overhead. But right now your function costs nothing except the overhead. – Ben Voigt Sep 28 '16 at 20:52
  • 5
    Imagine you are R and CPP is your hard-working colleague. What's faster: repeating any sentence spoken to you back immediately or walking over to your colleague and asking them to do it for you? Now, change "repeating a sentence" to "doing tax returns" and things might turn out different. (Disclaimer: I've never tried to do tax returns in C++. It may be faster, but is probably also more complicated than doing it in R.) – Jeroen Mostert Sep 28 '16 at 21:03
  • @JeroenMostert As a PhD student, my tax returns are pretty easy to handle ;) Thanks for the metaphor! – Remi.b Sep 28 '16 at 21:08
  • Thanks! I don't know much about these 'function interface' and never heard of the concept of 'overhead' for function call. I can see is that when I enter `cppFun`, it is written `.Primitive(".Call")(, x)`. Why does it take longer to call such function? – Remi.b Sep 28 '16 at 21:11
  • 2
    @Remi.b: I look forward to your faster implementation. Once you have that done, would you mind working on a time machine and a [perpertuum mobile](https://en.wikipedia.org/wiki/Perpetuum_mobile) too? – Dirk Eddelbuettel Sep 28 '16 at 21:29
  • 2
    @DirkEddelbuettel Hum... I do not understand the reason of this sarcasm. Do you mean that my follow-up in the comments is too broad? (I am already using Rcpp for much more complicated processes, I was just wondering whether calling a cpp function many times would come at a cost). – Remi.b Sep 28 '16 at 21:40

1 Answers1

12

This simply is not a well-posed or thought-out question as the comments above indicate.

The supposed baseline of an empty function simply is not one. Every function created via cppFunction() et al will call one R function interfacing to some C++ function. So this simply cannot be equal.

Here is a slightly more meaningful comparison. For starters, let's make the R function complete with curlies. Second, let's call another compiler (internal) function:

require(Rcpp)
require(microbenchmark)

cppFunction('int cppFun (int x) {return x;}')
RFun1 <- function(x) { x }
RFun2 <- function(x) { .Primitive("abs")(x) }

print(microbenchmark(RFun1(2L), RFun2(2L), cppFun(2L), times=1e5))

On my box, I see a) a closer gap between versions 1 and 2 (or the C++ function) and b) little penalty over the internal function. But calling ANY compiled function from R has cost.

Unit: nanoseconds
       expr min   lq     mean median   uq     max neval
  RFun1(2L) 171  338  434.136    355  408 2659984 1e+05
  RFun2(2L) 683  937 1334.046   1257 1341 7605628 1e+05
 cppFun(2L) 721 1131 1416.091   1239 1385 8544656 1e+05

As we say in the real world: there ain't no free lunch.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • Oh that makes complete sense. Is this the "overhead" @BenVoigt is referring to in the comments? Thanks – Remi.b Sep 28 '16 at 22:08
  • 1
    Yes, among other things. `cppFun()` and `RFun2()` actually have a body with code to execute. `RFun1()` does not. How could they possibly be equal in run-time? You are also making a rookie profiling mistake in trying to measure "empty". This really does not make too much sense as posed. – Dirk Eddelbuettel Sep 28 '16 at 22:16
  • Ok, I think it makes sense to me +1. Thanks a lot! – Remi.b Sep 28 '16 at 22:20