[Rd] profiler and loops
Romain Francois
romain.francois at dbmail.com
Tue Mar 3 17:23:36 CET 2009
Hello,
Please find attached a patch against svn implementing this proposal.
The part I don't fully understand is the part involving the function
loopWithContect, so I've put "[loop]" in there instead of "[for]",
"[while]" or "[repeat]" because I don't really know how to extract the
information.
With the script1 from my previous post, summaryRprof produces this:
[]$ /home/romain/workspace/R-trunk/bin/Rscript script1.R
$by.self
self.time self.pct total.time total.pct
"[for]" 5.32 98.9 5.38 100.0
"rnorm" 0.06 1.1 0.06 1.1
$by.total
total.time total.pct self.time self.pct
"[for]" 5.38 100.0 5.32 98.9
"rnorm" 0.06 1.1 0.06 1.1
$sampling.time
[1] 5.38
Romain
Romain Francois wrote:
> Hello,
>
> (This is follow up from this thread:
> http://www.nabble.com/execution-time-of-.packages-td22304833.html but
> with a different focus)
>
> I am often confused by the result of the profiler, when a loop is
> involved. Consider these two scripts:
>
> script1:
>
> Rprof( )
> x <- numeric( )
> for( i in 1:10000){
> x <- c( x, rnorm(10) )
> }
> Rprof( NULL )
> print( summaryRprof( ) )
>
>
> script2:
>
> Rprof( )
> ffor <- function(){
> x <- numeric( )
> for( i in 1:10000){
> x <- c( x, rnorm(10) )
> }
> }
> ffor()
> Rprof( NULL )
> print( summaryRprof( ) )
>
>
> []$ time Rscript --vanilla script1.R
> $by.self
> self.time self.pct total.time total.pct
> "rnorm" 0.22 100 0.22 100
>
> $by.total
> total.time total.pct self.time self.pct
> "rnorm" 0.22 100 0.22 100
>
> $sampling.time
> [1] 0.22
>
> real 0m7.786s
> user 0m5.192s
> sys 0m0.735s
>
> []$$ time Rscript --vanilla script2.R
> $by.self
> self.time self.pct total.time total.pct
> "ffor" 4.94 92.5 5.34 100.0
> "rnorm" 0.40 7.5 0.40 7.5
>
> $by.total
> total.time total.pct self.time self.pct
> "ffor" 5.34 100.0 4.94 92.5
> "rnorm" 0.40 7.5 0.40 7.5
>
> $sampling.time
> [1] 5.34
>
>
> real 0m7.841s
> user 0m5.152s
> sys 0m0.712s
>
>
>
> In the first one, I call a for loop from the top level and in the
> second one, the loop is wrapped in a function call. This shows the
> inability of the profiler to point loops as responsible for
> bottlenecks. The coder of script1 would not know what to do to improve
> on the script.
>
> I have had a quick look in the code, and here are a few thoughts:
>
> in the function "doprof" in eval.c, this loop write the call stack on
> the profiler file:
>
> for (cptr = R_GlobalContext; cptr; cptr = cptr->nextcontext) {
> if ((cptr->callflag & (CTXT_FUNCTION | CTXT_BUILTIN))
> && TYPEOF(cptr->call) == LANGSXP) {
> SEXP fun = CAR(cptr->call);
> if (!newline) newline = 1;
> fprintf(R_ProfileOutfile, "\"%s\" ",
> TYPEOF(fun) == SYMSXP ? CHAR(PRINTNAME(fun)) :
> "<Anonymous>");
> }
> }
> so we can see it only cares about context CTXT_FUNCTION and
> CTXT_BUILTIN, when for loops play with CTXT_LOOP (this is again in
> eval.c within the do_for function)
>
> begincontext(&cntxt, CTXT_LOOP, R_NilValue, rho, R_BaseEnv, R_NilValue,
> R_NilValue);
>
> which as the name implies, begins the context of the for loop. The
> begincontext function looks like this :
>
> void begincontext(RCNTXT * cptr, int flags,
> SEXP syscall, SEXP env, SEXP sysp,
> SEXP promargs, SEXP callfun)
> {
> cptr->nextcontext = R_GlobalContext;
> cptr->cstacktop = R_PPStackTop;
> cptr->evaldepth = R_EvalDepth;
> cptr->callflag = flags;
> cptr->call = syscall;
> cptr->cloenv = env;
> cptr->sysparent = sysp;
> cptr->conexit = R_NilValue;
> cptr->cend = NULL;
> cptr->promargs = promargs;
> cptr->callfun = callfun;
> cptr->vmax = vmaxget();
> cptr->intsusp = R_interrupts_suspended;
> cptr->handlerstack = R_HandlerStack;
> cptr->restartstack = R_RestartStack;
> cptr->prstack = R_PendingPromises;
> #ifdef BYTECODE
> cptr->nodestack = R_BCNodeStackTop;
> # ifdef BC_INT_STACK
> cptr->intstack = R_BCIntStackTop;
> # endif
> #endif
> R_GlobalContext = cptr;
> }
>
>
> So it could be possible to set the last argument of the begincontext
> function to "for" and use this code in the doprof function:
>
>
> for (cptr = R_GlobalContext; cptr; cptr = cptr->nextcontext) {
> if ( ( cptr->callflag & (CTXT_FUNCTION | CTXT_BUILTIN ) )
> && TYPEOF(cptr->call) == LANGSXP) {
> SEXP fun = CAR(cptr->call);
> if (!newline) newline = 1;
> fprintf(R_ProfileOutfile, "\"%s\" ",
> TYPEOF(fun) == SYMSXP ? CHAR(PRINTNAME(fun)) :
> "<Anonymous>");
> } else if( cptr->callflag & CTXT_LOOP){
> SEXP fun = CAR(cptr->syscall);
> if (!newline) newline = 1;
> fprintf(R_ProfileOutfile, "\"%s\" ", CHAR(PRINTNAME(fun)) );
> }
> }
>
> so that we see for in the list of "functions" that appear in the
> profiler file.
>
> Obviously I am taking some shortcuts here, because of the other loops,
> but I would like to make a formal patch with this. Before I do that,
> I'd like to know :
> - is this has a chance of breaking something else (does the CTXT_LOOP
> being R_NilValue is used elsewhere)
> - would this feature be welcome.
> - Should I differentiate real functions with loops in the output file,
> maybe I can write "[for]" instead of for to emphacize this is not a
> function.
>
> Romain
>
--
Romain Francois
Independent R Consultant
+33(0) 6 28 91 30 30
http://romainfrancois.blog.free.fr
-------------- next part --------------
A non-text attachment was scrubbed...
Name: profilerWithLoop.diff
Type: text/x-patch
Size: 1715 bytes
Desc: not available
URL: <https://stat.ethz.ch/pipermail/r-devel/attachments/20090303/fcc638a4/attachment.bin>
More information about the R-devel
mailing list