I am building an app that frequently computes the regularized incomplete beta function. The app is written in C++ and calls R::pbeta()
. When I tried to multithread the app, some warning messages from R::pbeta()
smashed the stack.
So I turned to boost::math::ibeta()
. Everything worked fine until I measured the speed. The following C++ file whyIsBoostSlower.cpp
implements the regularized incomplete beta function using either R::pbeta()
or boost::math::ibeta()
.
// [[Rcpp::plugins(cpp17)]]
#include <boost/math/special_functions/beta.hpp>
// [[Rcpp::depends(BH)]]
#include <Rcpp.h>
using namespace Rcpp;
// Compute the regularized incomplete Beta function.
// [[Rcpp::export]]
NumericVector RIBF(NumericVector q, NumericVector a, NumericVector b,
bool useboost = false)
{
NumericVector rst(q.size());
for (int i = 0, iend = q.size(); i < iend; ++i)
{
if (useboost) rst[i] = boost::math::ibeta( a[i], b[i], q[i] );
else rst[i] = R::pbeta( q[i], a[i], b[i], 1, 0 );
}
return rst;
}
In R, we measure the speed of calling the function 300000 times on random numbers:
Rcpp::sourceCpp("whyIsBoostSlower.cpp")
set.seed(123)
N = 300000L
q = runif(N) # Generate quantiles.
a = runif(N, 0, 10) # Generate a in (0, 10)
b = runif(N, 0, 10) # Generate b in (0, 10)
# Use R's pbeta(). This function calls a C wrapper of toms708.c:
# https://svn.r-project.org/R/trunk/src/nmath/toms708.c
system.time({ Rrst = RIBF(q, a, b, useboost = F) })
# Windows 10 (seconds):
# user system elapsed
# 0.11 0.00 0.11
# RedHat Linux:
# user system elapsed
# 0.097 0.000 0.097
# Use Boost's implementation, which also depends on TOMS 708 by their claim:
# https://www.boost.org/doc/libs/1_41_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_beta/ibeta_function.html
system.time({ boostRst = RIBF(q, a, b, useboost = T) })
# Windows 10:
# user system elapsed
# 0.52 0.00 0.52
# RedHat Linux:
# user system elapsed
# 0.988 0.001 0.993
range(Rrst - boostRst)
# -1.221245e-15 1.165734e-15
To reproduce the example, one needs to install R and package Rcpp
. On Windows, one also needs to install Rtools
which contains a GCC distribution. The optimization flag is default to -O2
.
Both R::pbeta()
and boost::math::ibeta()
are based on ACM TOMS 708, yet boost::math::ibeta()
is 5x slower on Windows and 10x slower on Linux.
I think it might have something to do with setting the Policy
argument in boost::math::ibeta()
, but how?
Thank you!
FYI, R::pbeta()
is defined in R-4.2.3/src/nmath/pbeta.c
. R::pbeta()
calls bratio()
which is defined in R-4.2.3/src/nmath/toms708.c
, namely https://svn.r-project.org/R/trunk/src/nmath/toms708.c . Code inside is C translation of TOMS 708 Fortran code. The translation is done by R's core team.
In contrast, Boost states "This implementation is closely based upon "Algorithm 708; Significant digit computation of the incomplete beta function ratios", DiDonato and Morris, ACM, 1992." on boost::math::ibeta()