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
| >| >
|