Thanks, that&#39;s very helpful. I&#39;ll experiment with this and report back if things speed up substantially. <br><br>Richard<br><br><br><div class="gmail_quote">On Mon, Dec 27, 2010 at 9:58 AM, Douglas Bates <span dir="ltr">&lt;<a href="mailto:bates@stat.wisc.edu">bates@stat.wisc.edu</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">My apologies, I accidentally hit send on an incomplete message.<br>
<div><div></div><div class="h5"><br>
On Mon, Dec 27, 2010 at 8:34 AM, Douglas Bates &lt;<a href="mailto:bates@stat.wisc.edu">bates@stat.wisc.edu</a>&gt; wrote:<br>
&gt; On Mon, Dec 27, 2010 at 6:57 AM, Richard Chandler<br>
&gt; &lt;<a href="mailto:richard.chandlers@gmail.com">richard.chandlers@gmail.com</a>&gt; wrote:<br>
&gt;&gt; Hi,<br>
&gt;&gt;<br>
&gt;&gt; I am trying to speed up a vectorized R function without much luck. Perhaps<br>
&gt;&gt; it is not possible, but if it is could someone suggest how to improve the<br>
&gt;&gt; C++/Rcpp code below? The speed is approximately equal on a 64-bit version of<br>
&gt;&gt; R, but the R code is much faster when using 32-bit R (on windows). Here&#39;s an<br>
&gt;&gt; example.<br>
&gt;<br>
&gt; You are probably correct that there isn&#39;t much to be gained in going<br>
&gt; to compiled code relative to the vectorized R code.  It is likely that<br>
&gt; the majority of the execution time is the actual numerical evaluation,<br>
&gt; not the looping overhead.  However, if you want to try to improve the<br>
&gt; execution time of the compiled code you should look analyze your inner<br>
&gt; loop, as described below.<br>
&gt;<br>
&gt;&gt;  # R function<br>
&gt;&gt; nllR &lt;- function(y, psi, p, nd) {<br>
&gt;&gt;         cp &lt;- (p^y) * ((1 - p)^(1 - y))<br>
&gt;&gt;         cp &lt;- exp(rowSums(log(cp))) # faster than: apply(cp, 1, prod)<br>
&gt;&gt;     loglik &lt;- log(cp * psi + nd * (1 - psi))<br>
&gt;&gt;         -sum(loglik)<br>
&gt;&gt;     }<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; # Rcpp function<br>
&gt;&gt; nllRcpp &lt;- cxxfunction(signature(yR=&quot;matrix&quot;, psiR=&quot;numeric&quot;, pR=&quot;matrix&quot;,<br>
&gt;&gt;     ndR=&quot;integer&quot;),<br>
&gt;&gt;     &#39;<br>
&gt;&gt;     NumericMatrix y(yR);<br>
&gt;&gt;     NumericVector psi(psiR);<br>
&gt;&gt;     NumericMatrix p(pR);<br>
&gt;&gt;     IntegerVector nd(ndR);<br>
&gt;&gt;<br>
&gt;&gt;     NumericVector yV(y);<br>
&gt;&gt;     int R = y.nrow();<br>
&gt;&gt;     int J = y.ncol();<br>
&gt;&gt;     NumericMatrix cp(R, J);<br>
&gt;&gt;     NumericVector ll(R);<br>
&gt;&gt;     NumericVector cpsum(R);<br>
&gt;&gt;<br>
&gt;&gt;     for(int i=0; i&lt;R; i++) {<br>
&gt;&gt;         for(int j=0; j&lt;J; j++) {<br>
&gt;&gt;             cp(i,j) = pow(p(i,j), y(i,j)) * pow(1-p(i,j), 1-y(i,j));<br>
&gt;&gt;             }<br>
&gt;&gt;         NumericVector cpi = cp(i, _);<br>
&gt;&gt;         cpsum[i] = exp(sum(log(cpi)));<br>
&gt;&gt;         ll[i] = log(cpsum[i] * psi[i] + nd[i] * (1-psi[i]));<br>
&gt;&gt;         }<br>
&gt;&gt;     double nll = -1*sum(ll);<br>
<br>
&gt; Notice that you are going back and forth between the logarithm scale<br>
&gt; and the original scale twice (the pow function needs to take the<br>
&gt; logarithm then multiply by the power then exponentiate the result).<br>
&gt; If you want to return the log likelihood then start with log(p(i,j))<br>
&gt; and log(1 - p(i,j)) and rewrite the rest of the calculation on the<br>
&gt; logarithm scale.  Your definition of cpsum[i] exponentiates the sum of<br>
&gt; the logarithms then, in the next statement, takes the logarithm of a<br>
&gt; product involving that term.<br>
&gt;<br>
&gt; Also you should realize that y(i,j) and p(i,j) are not without cost so<br>
&gt; cache the values once you obtain them (it is likely that the compiler<br>
&gt; will perform this optimization anyway but there is little cost to<br>
&gt; writing the loop effectively in the first place).  And you don&#39;t<br>
&gt; really need to allocate the storage for cp, ll and cpsum because you<br>
&gt; are not saving these values.  Finally, there is an advantage in using<br>
&gt; the pointers to the array contents rather than indexing and in running<br>
&gt; down the rows rather than across columns.<br>
&gt;<br>
&gt; Doing all those things at once is perhaps unnecessary and may not be<br>
&gt; worthwhile.  Let&#39;s start with evaluating log(cp) then log(cpsum).<br>
&gt;<br>
&gt; NumericMatrix logcp(NumericMatrix p, NumericMatrix y) throw (std::exception) {<br>
&gt;    int nr = p.nrow(), nc = p.ncol();<br>
&gt;<br>
&gt;    if (y.nrow() != nr || y.ncol() != nc()) throw<br>
&gt; std::exception(&quot;dimension mismatch&quot;);<br>
&gt;    NumericMatrix ans = clone(y);<br>
&gt;<br>
&gt;    // Can write the next part as a loop<br>
&gt;    double *ap = ans.begin(), *pp = p.begin(), *yp = y.begin();<br>
&gt;    int ntot = nr * nc;<br>
&gt;    for (int i = 0; i &lt; ntot; i++, ap++,) {<br>
</div></div>       *ap = *yp * log(*pp) + (1 - *yp) * log(1 - *pp);<br>
    }<br>
    return ans;<br>
<br>
or you could use std::transform, by first defining a transformation<br>
function of the form<br>
double logcpscalar(double p, double y) {return y * log(p) + (1-y) * log(1-p);}<br>
<br>
and replacing the entire loop by<br>
   std::transform(p.begin(), p.end(), y.begin(), ans.begin(), logcpscalar);<br>
<br>
Of course, if you will only ever use the row sums of the logarithms<br>
then you could accumulate them while evaluating the elements and not<br>
bother storing the elements at all.  It would look like<br>
<br>
NumericVector rowsumsLog(&gt; NumericMatrix p, NumericMatrix y) throw<br>
<div class="im">(std::exception) {<br>
    int nr = p.nrow(), nc = p.ncol();<br>
<br>
    if (y.nrow() != nr || y.ncol() != nc()) throw<br>
std::exception(&quot;dimension mismatch&quot;);<br>
</div>    NumericVector ans(nr, 0.);    // ensure that it is initialized to zeros<br>
<div class="im">    double *ap = ans.begin(), *pp = p.begin(), *yp = y.begin();<br>
<br>
</div>    for (int j = 0; j &lt; nc; j++)<br>
        for (int i = 0; i &lt; nr; i++, pp++, yp++)<br>
            ap[i] += *yp * log(*pp) + (1 - *yp) * log(1 - *pp);<br>
<br>
    return ans;<br>
<div><div></div><div class="h5"><br>
<br>
&gt;&gt;     return wrap(nll);<br>
&gt;&gt;     &#39;, plugin=&quot;Rcpp&quot;)<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; # Test<br>
&gt;&gt; M &lt;- 10000<br>
&gt;&gt; J &lt;- 100<br>
&gt;&gt; psi &lt;- runif(M)<br>
&gt;&gt; Z &lt;- rbinom(M, 1, psi)<br>
&gt;&gt; p &lt;- matrix(runif(M*J), M, J)<br>
&gt;&gt; y &lt;- matrix(rbinom(M*J, 1, p*Z), M, J)<br>
&gt;&gt; nd &lt;- ifelse(rowSums(y, na.rm=TRUE) == 0, 1, 0)<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; all.equal(nllR(y, psi, p, nd), nllRcpp(y, psi, p, nd))<br>
&gt;&gt;<br>
&gt;&gt; # 64-bit<br>
&gt;&gt;&gt; benchmark(nllR(y, psi, p, nd), nllRcpp(y, psi, p, nd))<br>
&gt;&gt;                     test replications elapsed relative user.self<br>
&gt;&gt; 1    nllR(y, psi, p, nd)          100   10.58 1.046489      9.35<br>
&gt;&gt; 2 nllRcpp(y, psi, p, nd)          100   10.11 1.000000      9.81<br>
&gt;&gt;   sys.self user.child sys.child<br>
&gt;&gt; 1     1.14         NA        NA<br>
&gt;&gt; 2     0.26         NA        NA<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; # 32-bit<br>
&gt;&gt;&gt; benchmark(nllR(y, psi, p, nd), nllRcpp(y, psi, p, nd))<br>
&gt;&gt;                     test replications elapsed relative user.self<br>
&gt;&gt; 1    nllR(y, psi, p, nd)          100   16.75 1.000000     15.54<br>
&gt;&gt; 2 nllRcpp(y, psi, p, nd)          100   29.97 1.789254     29.81<br>
&gt;&gt;   sys.self user.child sys.child<br>
&gt;&gt; 1     1.08         NA        NA<br>
&gt;&gt; 2     0.01         NA        NA<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;&gt; sessionInfo()<br>
&gt;&gt; R version 2.12.0 (2010-10-15)<br>
&gt;&gt; Platform: x86_64-pc-mingw32/x64 (64-bit)<br>
&gt;&gt;<br>
&gt;&gt; ...<br>
&gt;&gt;<br>
&gt;&gt; attached base packages:<br>
&gt;&gt; [1] stats     graphics  grDevices utils     datasets  methods<br>
&gt;&gt; [7] base<br>
&gt;&gt;<br>
&gt;&gt; other attached packages:<br>
&gt;&gt; [1] rbenchmark_0.3       inline_0.3.8         RcppArmadillo_0.2.10<br>
&gt;&gt; [4] Rcpp_0.9.0<br>
&gt;&gt;<br>
&gt;&gt; loaded via a namespace (and not attached):<br>
&gt;&gt; [1] tools_2.12.0<br>
&gt;&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; Thanks,<br>
&gt;&gt; Richard<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; _______________________________________________<br>
&gt;&gt; Rcpp-devel mailing list<br>
&gt;&gt; <a href="mailto:Rcpp-devel@lists.r-forge.r-project.org">Rcpp-devel@lists.r-forge.r-project.org</a><br>
&gt;&gt; <a href="https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel" target="_blank">https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel</a><br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;<br>
</div></div></blockquote></div><br>