Fast way to call an R function from C++?

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Fast way to call an R function from C++?

Wang Jiefei
Hi,

I'm looking for a most efficient way to call an R function from C++ in a
package. I know there are two functions (`R_forceAndCall` and `Rf_eval`)
that can do the "call" part, but both are slow compared to calling the same
function in R. I also try to use Rcpp and it is the worse one. Here is my
test code:

C++ code:
```
// [[Rcpp::export]]
SEXP C_test1(SEXP f, SEXP x) {
SEXP call =PROTECT(Rf_lang2(f, x));
SEXP val = R_forceAndCall(call, 1, R_GlobalEnv);
UNPROTECT(1);
return val;
}

// [[Rcpp::export]]
SEXP C_test2(SEXP expr, SEXP env) {
SEXP val = Rf_eval(expr, env);
return val;
}

// [[Rcpp::export]]
SEXP C_test3(SEXP f,SEXP x) {
Function fun(f);
return fun(x);
}
```

R code:
```
testFunc<-function(x){
  x=x^2
  return(x)
}
evn=new.env()
evn$x=x
expr=quote(testFunc(evn$x))

testFunc(evn$x)
C_test1(testFunc, evn$x)
C_test2(expr,evn)
C_test3(testFunc,evn$x)
```

For the results, I run each function 1,000,000 times:

   - testFunc : 0.47 sec
   - C_test1 : 2.46 sec
   - C_test2 : 2.74 sec
   - C_test3 : 18.86 sec

It is clear to see that calling an R function in R is the fast one, it is
about 5X faster than ` R_forceAndCall ` and ` Rf_eval`. the latter two
functions have a similar performance and using Rcpp is the worst one. Is it
expected? Why is calling an R function from C++ much slower than calling
the function from R? Is there any faster way to do the function call in C++?

Best,
Jiefei

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Fast way to call an R function from C++?

Kevin Ushey
Hi Jiefei,

Calling into R from C++ code is more complicated than one might think.
Please see Tomas Kalibera's post here:
https://developer.r-project.org/Blog/public/2019/03/28/use-of-c---in-packages/index.html

The Rcpp Function class is more expensive than a regular Rf_eval()
because it tries to prevent errors (longjmps) from jumping to the top
level, so that the C++ stack can be properly unwound. In addition,
your example also pays a cost for the indirect function calls used
with Rcpp attributes, since the generated R wrapper functions will
then use `.Call()` under the hood. You could also try to directly use
.Call() to avoid that extra R function call overhead.

Even then, R's dispatch system for primitive functions (like `^`) is
likely still going to be faster than what you get from the .Call()
interface, especially since some extra validation does occur when
using .Call(). In practice, that difference is usually negligible
outside of synthetic benchmarks.

Best,
Kevin

On Tue, Jun 18, 2019 at 10:41 AM King Jiefei <[hidden email]> wrote:

>
> Hi,
>
> I'm looking for a most efficient way to call an R function from C++ in a
> package. I know there are two functions (`R_forceAndCall` and `Rf_eval`)
> that can do the "call" part, but both are slow compared to calling the same
> function in R. I also try to use Rcpp and it is the worse one. Here is my
> test code:
>
> C++ code:
> ```
> // [[Rcpp::export]]
> SEXP C_test1(SEXP f, SEXP x) {
> SEXP call =PROTECT(Rf_lang2(f, x));
> SEXP val = R_forceAndCall(call, 1, R_GlobalEnv);
> UNPROTECT(1);
> return val;
> }
>
> // [[Rcpp::export]]
> SEXP C_test2(SEXP expr, SEXP env) {
> SEXP val = Rf_eval(expr, env);
> return val;
> }
>
> // [[Rcpp::export]]
> SEXP C_test3(SEXP f,SEXP x) {
> Function fun(f);
> return fun(x);
> }
> ```
>
> R code:
> ```
> testFunc<-function(x){
>   x=x^2
>   return(x)
> }
> evn=new.env()
> evn$x=x
> expr=quote(testFunc(evn$x))
>
> testFunc(evn$x)
> C_test1(testFunc, evn$x)
> C_test2(expr,evn)
> C_test3(testFunc,evn$x)
> ```
>
> For the results, I run each function 1,000,000 times:
>
>    - testFunc : 0.47 sec
>    - C_test1 : 2.46 sec
>    - C_test2 : 2.74 sec
>    - C_test3 : 18.86 sec
>
> It is clear to see that calling an R function in R is the fast one, it is
> about 5X faster than ` R_forceAndCall ` and ` Rf_eval`. the latter two
> functions have a similar performance and using Rcpp is the worst one. Is it
> expected? Why is calling an R function from C++ much slower than calling
> the function from R? Is there any faster way to do the function call in C++?
>
> Best,
> Jiefei
>
>         [[alternative HTML version deleted]]
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Fast way to call an R function from C++?

Iñaki Ucar
In reply to this post by Wang Jiefei
On Tue, 18 Jun 2019 at 19:41, King Jiefei <[hidden email]> wrote:
>
> [...]
>
> It is clear to see that calling an R function in R is the fast one, it is
> about 5X faster than ` R_forceAndCall ` and ` Rf_eval`. the latter two
> functions have a similar performance and using Rcpp is the worst one. Is it
> expected? Why is calling an R function from C++ much slower than calling
> the function from R? Is there any faster way to do the function call in C++?

Yes, there is: enable fast evaluation by setting
-DRCPP_USE_UNWIND_PROTECT, or alternatively, use

// [[Rcpp::plugins(unwindProtect)]]

Iñaki

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Fast way to call an R function from C++?

Iñaki Ucar
For reference, your benchmark using UNWIND_PROTECT:

> system.time(test(testFunc, evn$x))
   user  system elapsed
  0.331   0.000   0.331
> system.time(test(C_test1, testFunc, evn$x))
   user  system elapsed
  2.029   0.000   2.036
> system.time(test(C_test2, expr, evn))
   user  system elapsed
  2.307   0.000   2.313
> system.time(test(C_test3, testFunc, evn$x))
   user  system elapsed
  2.131   0.000   2.138

Iñaki

On Tue, 18 Jun 2019 at 20:35, Iñaki Ucar <[hidden email]> wrote:

>
> On Tue, 18 Jun 2019 at 19:41, King Jiefei <[hidden email]> wrote:
> >
> > [...]
> >
> > It is clear to see that calling an R function in R is the fast one, it is
> > about 5X faster than ` R_forceAndCall ` and ` Rf_eval`. the latter two
> > functions have a similar performance and using Rcpp is the worst one. Is it
> > expected? Why is calling an R function from C++ much slower than calling
> > the function from R? Is there any faster way to do the function call in C++?
>
> Yes, there is: enable fast evaluation by setting
> -DRCPP_USE_UNWIND_PROTECT, or alternatively, use
>
> // [[Rcpp::plugins(unwindProtect)]]
>
> Iñaki



--
Iñaki Úcar

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Fast way to call an R function from C++?

Wang Jiefei
Hello Kevin and Iñaki,

Thanks for your quick responses. I sincerely appreciate them! I can see how
complicated it is to interact with R in C. Iñaki's suggestion is very
helpful, I saw there is a lot of performance gain by turning the flag on,
but sadly the best performance it can offer still cannot beat R itself. It
is interesting to see that C++ is worse than R in this special case despite
there is a common belief that C++ code is the fast one... Anyway, thanks
again for your suggestions and reference!

Best,
Jiefei


On Tue, Jun 18, 2019 at 2:39 PM Iñaki Ucar <[hidden email]> wrote:

> For reference, your benchmark using UNWIND_PROTECT:
>
> > system.time(test(testFunc, evn$x))
>    user  system elapsed
>   0.331   0.000   0.331
> > system.time(test(C_test1, testFunc, evn$x))
>    user  system elapsed
>   2.029   0.000   2.036
> > system.time(test(C_test2, expr, evn))
>    user  system elapsed
>   2.307   0.000   2.313
> > system.time(test(C_test3, testFunc, evn$x))
>    user  system elapsed
>   2.131   0.000   2.138
>
> Iñaki
>
> On Tue, 18 Jun 2019 at 20:35, Iñaki Ucar <[hidden email]> wrote:
> >
> > On Tue, 18 Jun 2019 at 19:41, King Jiefei <[hidden email]> wrote:
> > >
> > > [...]
> > >
> > > It is clear to see that calling an R function in R is the fast one, it
> is
> > > about 5X faster than ` R_forceAndCall ` and ` Rf_eval`. the latter two
> > > functions have a similar performance and using Rcpp is the worst one.
> Is it
> > > expected? Why is calling an R function from C++ much slower than
> calling
> > > the function from R? Is there any faster way to do the function call
> in C++?
> >
> > Yes, there is: enable fast evaluation by setting
> > -DRCPP_USE_UNWIND_PROTECT, or alternatively, use
> >
> > // [[Rcpp::plugins(unwindProtect)]]
> >
> > Iñaki
>
>
>
> --
> Iñaki Úcar
>

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Fast way to call an R function from C++?

Iñaki Ucar
On Wed, 19 Jun 2019 at 07:42, King Jiefei <[hidden email]> wrote:
>
> Hello Kevin and Iñaki,
>
> Thanks for your quick responses. I sincerely appreciate them! I can see how complicated it is to interact with R in C. Iñaki's suggestion is very helpful, I saw there is a lot of performance gain by turning the flag on, but sadly the best performance it can offer still cannot beat R itself. It is interesting to see that C++ is worse than R in this special case despite there is a common belief that C++ code is the fast one... Anyway, thanks again for your suggestions and reference!

That is misleading. C++ code is faster, that's beyond doubt. But you
are not running C++ code here, you are running R code. So what is
faster, running R code or executing something that then runs R code?
Obviously the first thing is the baseline, and from there, it can only
get worse as you add more layers on top of it.

Iñaki

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel