[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