[Rcpp-devel] Passing an exposed function to another C++ function as a parameter
Manuel Castejón Limas
manuel.castejon at gmail.com
Thu Aug 4 12:56:53 CEST 2011
>After having browse Rcpp sources for the possibility of accessing the
>pointer of the exposed function, I have reckon the complexity of the task.
>
>I've tried with Dirk's recommendation and I'm more than good. Let's start
>with a benchmarking.
>
># test elapsed relative
># 1 usingXPtrs 0.001 1
># 3 usingTanh 0.001 1
># 2 usingTanhf0 0.169 169
>
>The external pointer solution is as fast as the original code.
>The following lines describe the bechmarking.
>
>
>##########################################################################
>#
>####
>#Benchmarking
>##########################################################################
>#
>####
>
>otherCode <- '
> using namespace Rcpp;
> double f0(double inducedLocalField) {
> return( tanh(inducedLocalField) );
> }
> double f1(double inducedLocalField) {
> double tanhx = tanh(inducedLocalField);
> return( 1-tanhx*tanhx );
> }
>
> RCPP_MODULE(mod_Test) {
> function( "Tanh_f0", &f0 ) ;
> }
> '
>
>testCode <- '
> typedef double (*funcPtr)(double);
> return List::create( _["f0"]=XPtr<funcPtr>(new
>funcPtr(&f0)),
> _["f1"]=XPtr<funcPtr>(new
>funcPtr(&f1))
>) ;
> '
>
>testCodefun <- cxxfunction(sig = character(), body = testCode, includes =
>otherCode, plugin="Rcpp")
>
>functionPointers <- testCodefun()
>
> modTest <- Module("mod_Test", getDynLib(testCodefun))
>
>## Benchmarking
>
>testCode <- ' // ----------------- passing a function as an XPtr
>-------------
>
> typedef double (*funcPtr)(double);
> List functionPointers(listOfFunctionPointers);
> XPtr<funcPtr> f0XPtr = functionPointers["f0"];
> XPtr<funcPtr> f1XPtr = functionPointers["f1"];
> double xx=as<double>(x);
> int N =as<int>(n);
> double result;
> for (int i = 0; i<N ; i++) {
> result = (*f0XPtr)(xx);
> }
> return wrap( result );
> '
>
>usingXPtrs <- cxxfunction(sig =
>signature(listOfFunctionPointers="externalpointer", x="numeric",
>n="integer"), body = testCode, includes = otherCode, plugin="Rcpp")
>
>usingXPtrs(functionPointers, 0.4, 100)
># [1] 0.379949
>
>
>
>testCode <- ' // ----------------- passing a function exposed by Module
>-------
> Rcpp::Function fx (myfun);
> int N =as<int>(n);
> double result;
> for (int i = 0; i<N ; i++) {
> result = as<double>(fx(x));
> }
> return wrap( result );
> '
>usingTanhf0 <- cxxfunction(sig=signature(myfun="function", x="numeric",
>n="integer"), body = testCode, includes = otherCode, plugin="Rcpp")
>
>usingTanhf0(myfun=modTest$Tanh_f0, x=0.4, n=100)
># [1] 0.379949
>
>testCode <- ' // ----------------- using C++ tanh -------
>
> int N =as<int>(n);
> double result;
> for (int i = 0; i<N ; i++) {
> double value = as<double>(x);
> result = tanh(value);
> }
> return wrap( result );
> '
>usingTanh <- cxxfunction(sig=signature(x="numeric", n="integer"), body =
>testCode, includes = otherCode, plugin="Rcpp")
>
>usingTanh( x=0.4, n=100)
># [1] 0.379949
>
>suppressMessages(require("rbenchmark"))
>
> benchmark( usingXPtrs(functionPointers, 0.4, 100),
> usingTanhf0(myfun=modTest$Tanh_f0, x=0.4, n=100) ,
> usingTanh( x=0.4, n=100),
> columns=c("test", "elapsed", "relative"), order="relative",
>replications=100)
>
> # test elapsed
>relative
> # 1 usingXPtrs 0.001 1
> # 3 usingTanh 0.001 1
> # 2 usingTanhf0 0.169 169
>
>
>
>
>El 03/08/11 19:22, "Dirk Eddelbuettel" <edd at debian.org> escribi¨®:
>
>>
>>On 3 August 2011 at 18:59, Manuel Castej¨®n Limas wrote:
>>| Great! I'm eager to see the example. Thank you very much indeed!
>>|
>>| I'm sorry about the formatting of the table, I'm used to write on a
>>very
>>| wide setting so it looked ok on my computer. I'll be more careful the
>>next
>>| time.
>>|
>>| Though I did not mention it in the first message, the problem posed has
>>| paramount importance for the new version of the AMORE package and may
>>be
>>| for many others to come.
>>|
>>| The thing is that, in the case of AMORE ---a neural network
>>simulator---,
>>| the user of the package is provided the capability to write their own
>>| neuron activation functions and training cost functions. In the stable
>>| version on CRAN this is done by letting the user write those functions
>>in
>>| R language at the cost of a lower performance. Those users that are ok
>>| with the functions provided in the package enjoy the benefits of
>>compiled
>>| code; those writing their own functions have to cope with a lower speed
>>| unless they want to sink into the code of the package, but those are
>>the
>>| less I guess.
>>|
>>| Now, thanks to Rcpp, a see a new possibility which is to let the user
>>| write their own functions in C++ and use them along the provided ones
>>in
>>| the package following the scheme I was trying to get to work, and all
>>that
>>| without having to compromise performance.
>>
>>
>>As Romain mentioned, I addressed that with my 'port' of DEoptim to Rcpp
>>in
>>the RcppDE package. It allows for user-defined objective functions that
>>can
>>be either supplied in R or C. Simple examples are in a demo file in the
>>package, see demo(CompiledBenchmark) -- or more importantly, see its
>>source
>>and the RcppDE source.
>>
>>To take but one example, the 'Rastrigin' optimisation function is
>>written
>>(in
>>DEoptim and other places) as
>>
>> Rastrigin <- function(x) {
>> sum(x+2 - 10 * cos(2*pi*x)) + 20
>> }
>>
>>and we can supply a C++ variant as
>>
>> double rastrigin(SEXP xs) {
>> Rcpp::NumericVector x(xs);
>> int n = x.size();
>> double sum = 20.0;
>> for (int i=0; i<n; i++) {
>> sum += x[i]+2 - 10*cos(2*M_PI*x[i]);
>> }
>> return(sum);
>>
>>from which we can build an XPtr object (where we actually switch over
>>three
>>different possibilities, here simplified):
>>
>> typedef double (*funcPtr)(SEXP);
>> return(XPtr<funcPtr>(new funcPtr(&rastrigin)));
>>
>>and that XPtr is then passed down to the C++ code to be evaluated.
>>
>>Have a look at the demo, it may get you towards where you want to go.
>>This
>>is still pretty raw and drafty code in RcppDE; I stopped when it started
>>to
>>do what I wanted it to do.
>>
>>Cheers, Dirk
>>
>>
>>|
>>| The point is that the Tanh_f0 function, shown as an example, or many
>>| others will be written by the user if need be, while the rest of the
>>code
>>| involved in training the network will be provided by the package.
>>|
>>| There's beauty indeed in being able to merge those two worlds.
>>|
>>| Similarly, the same thing might be done with classes. A package could
>>then
>>| provide a framework and the users could extend the existing classes
>>| customizing the behavior of the package with the ease of using inline.
>>Now
>>| I'm thinking about training algorithms in the case of AMORE.
>>|
>>| But that's another story. For now, I'm dreaming on the example that
>>Romain
>>| is cooking.
>>|
>>| PS: Did I mention you guys did a great job with Rcpp! I love it. It's a
>>| revolutionary package in the R arena, IMHO.
>>|
>>|
>>|
>>| El 03/08/11 15:29, "Dirk Eddelbuettel" <edd at debian.org> escribi¨®:
>>|
>>| >
>>| >On 3 August 2011 at 14:42, Romain Francois wrote:
>>| >| Hi,
>>| >|
>>| >| This looks very similar to a problem Dirk faced in rewriting the
>>| >DEOptim
>>| >| package to use Rcpp (the RcppDE package is on the Rcpp svn repo).
>>| >
>>| >(and on CRAN)
>>| >
>>| >| Essentially the problem is that the Rcpp::Function is a C++ object
>>that
>>| >| calls back to R, with additional protection, which eventually calls
>>C++.
>>| >|
>>| >| You should be able to bypass this entirely and access directly the
>>| >| pointer to the C++ function from the R variable. This way, you stay
>>in
>>| >| the C++ world. This would allow you to branch your code depending on
>>| >| whether you loop over a pure R function or a C++ function.
>>| >|
>>| >| I'll cook an example.
>>| >
>>| >Cool, thanks. Maybe add it to the Rcpp-FAQ vignette too?
>>| >
>>| >Dirk
>>| >
>>| >|
>>| >| Romain
>>| >|
>>| >| Le 03/08/11 08:22, Manuel Castej¨®n Limas a ¨¦crit :
>>| >| >
>>| >| >> Dear all,
>>| >| >>
>>| >| >> I'm rewriting the AMORE package using Rcpp --- in fact it's more
>>like
>>| >| >> I'm having a lot of fun while rewriting the AMORE package thanks
>>to
>>| >Rcpp.
>>| >| >> Nevertheless, I'm facing this little problem that I hope it
>>would
>>be
>>| >| >> pretty easy for you to solve.
>>| >| >>
>>| >| >> Let's consider the C++ function Tanh_f0
>>| >| >>
>>| >| >> double
>>| >| >>
>>| >| >> Tanh_f0(double inducedLocalField)
>>| >| >>
>>| >| >> {
>>| >| >>
>>| >| >> return tanh(inducedLocalField);
>>| >| >>
>>| >| >> }
>>| >| >>
>>| >| >>
>>| >| >> After compilation using inline, Tanh_f0 is exposed to R using
>>Module
>>| >| >> and accessible through actMod$Tanh_f0
>>| >| >>
>>| >| >>
>>| >| >> actMod$Tanh_f0(0.1)
>>| >| >>
>>| >| >> # [1] 0.09966799
>>| >| >>
>>| >| >>
>>| >| >> Now, I want to pass actMod$Tanh_f0 as a parameter of a function
>>in
>>| >| >> order to use the original C++ function Tanh_f0.
>>| >| >>
>>| >| >>
>>| >| >>
>>| >| >> testCode <- ' Rcpp::Function fx (myfun);
>>| >| >>
>>| >| >> double result = as<double>(fx(x));
>>| >| >>
>>| >| >> return wrap( result );
>>| >| >>
>>| >| >> '
>>| >| >>
>>| >| >> usingTanhf0 <- cfunction(sig=signature(myfun="function",
>>| >x="numeric"),
>>| >| >> body=testCode,)
>>| >| >>
>>| >| >>
>>| >| >> And that indeed works, but when compared to using a simple tanh
>>| >| >> function it shows quite a bad performance.
>>| >| >>
>>| >| >>
>>| >| >>
>>| >| >> testCode <- ' double value = as<double>(x);
>>| >| >>
>>| >| >> double result = tanh(value);
>>| >| >>
>>| >| >> return wrap( result );
>>| >| >>
>>| >| >> '
>>| >| >>
>>| >| >>
>>| >| >> usingTanh <- cfunction(sig=signature(x="numeric"),
>>body=testCode,
>> )
>>| >| >>
>>| >| >> benchmark(usingTanhf0(myfun=actMod$Tanh_f0, x=0.1) , usingTanh(
>>| >| >> x=0.1), columns=c("test", "replications", "elapsed", "relative"),
>>| >| >> order="relative", replications=1000)
>>| >| >>
>>| >| >> # test replications
>>elapsed
>>| >| >> relative
>>| >| >>
>>| >| >> # 2 usingTanh(x = 0.1) 1000
>>0.004
>>| >| >> 1
>>| >| >>
>>| >| >> # 1 usingTanhf0(myfun = actMod$Tanh_f0, x = 0.1) 1000
>>0.080
>>| >| >> 20
>>| >| >>
>>| >| >>
>>| >| >> Looks like having to go the R way to get access to Tanh_f0 has a
>>high
>>| >| >> cost. In order to be faster, it would be great if I could have
>>access
>>| >| >> to the original Tanh_f0 which I guess is pointed to by
>>| >| >> actMod$Tanh_f0, may be at the address 0x100153d30 ?
>>| >| >>
>>| >| >>
>>| >| >> actMod$Tanh_f0
>>| >| >>
>>| >| >> # internal C++ function <0x100153d30>
>>| >| >>
>>| >| >> # signature : double Tanh_f0(double)
>>| >| >>
>>| >| >>
>>| >| >> After having had a look at RppDE sources, I guess the solution
>>would
>>| >| >> be to pass the function as an external pointer; something like :
>>| >| >>
>>| >| >> testCode <- '
>>| >| >>
>>| >| >> typedef double (*funPtr)(double) ;
>>| >| >>
>>| >| >> Rcpp::XPtr< funPtr > fx (myfun);
>>| >| >>
>>| >| >> double result = (*fx)(0.1);
>>| >| >>
>>| >| >> return wrap( result );
>>| >| >>
>>| >| >> '
>>| >| >>
>>| >| >> testCodefun <- cfunction(sig=signature(myfun="C++Function"),
>>| >| >> body=testCode,)
>>| >| >>
>>| >| >> result <- testCodefun(myfun=actMod$Tanh_f0)
>>| >| >>
>>| >| >>
>>| >| >> But this does NOT work and crashes R.
>>| >| >>
>>| >| >> Any hints?
>>| >| >>
>>| >| >> Thank you for your patience in reading such long message!
>>| >| >> Manuel
>>| >| >>
>>| >| >>
>>| >| >>
>>| >| >
>>| >| >
>>| >| > _______________________________________________
>>| >| > Rcpp-devel mailing list
>>| >| > Rcpp-devel at lists.r-forge.r-project.org
>>| >| >
>>|
>>>https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel
>>| >|
>>| >|
>>| >| --
>>| >| Romain Francois
>>| >| Professional R Enthusiast
>>| >| +33(0) 6 28 91 30 30
>>| >| http://romainfrancois.blog.free.fr
>>| >| http://romain-francois.com
>>| >| |- http://bit.ly/lJoWbH : Montpellier Com¨¦die Club - Juin 2011
>>| >| |- http://bit.ly/kaSV6U : Stand up set at Up The Creek
>>| >| `- http://bit.ly/hdKhCy : Rcpp article in JSS
>>| >|
>>| >|
>>| >| _______________________________________________
>>| >| Rcpp-devel mailing list
>>| >| Rcpp-devel at lists.r-forge.r-project.org
>>| >|
>>https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel
>>| >
>>| >--
>>| >Gauss once played himself in a zero-sum game and won $50.
>>| > -- #11 at http://www.gaussfacts.com
>>|
>>|
>>
>>--
>>Gauss once played himself in a zero-sum game and won $50.
>> -- #11 at http://www.gaussfacts.com
More information about the Rcpp-devel
mailing list