Motivation
R uses default GCC/Clang compiler optimizations, which can limit the performance of C++ code.
User-wide Makevars
You can use the following Makevars example, which should be placed in
~/.R/Makevars
, to set the optimization level to
-O3
for all C++ code compiled by R and use additional flags
to enable more aggressive optimizations that can be set at setup level
but not package level (or CRAN will reject the package):
# Override R's optimization level
CXXFLAGS = -g -O3 -march=native
CXX11FLAGS = -g -O3 -march=native
CXX14FLAGS = -g -O3 -march=native
CXX17FLAGS = -g -O3 -march=native
# Additional optimizations
CXXFLAGS += -funroll-loops -ftree-vectorize -fprefetch-loop-arrays
CXXFLAGS += -fomit-frame-pointer -fstrict-aliasing
# Link-time optimization
CXXFLAGS += -flto=auto
LDFLAGS += -flto=auto
Anticonf scripts
The idea of an “anticonf” configure script is to provide a tailored
Makevars file with the understanding that a package will be run on
different systems. The anticonf name comes from the fact that it is not
a standard configure
script created with tools such as GNU
Autoconf, but rather a templated script that generates a
Makevars
by detecting the system and compiler settings
(e.g., number of cores, optimization flags, etc.).
The capybara
package has the following Makevars.in
file:
CXX_STD ?= CXX11 CXX14 CXX17
PKG_CXXFLAGS = -DARMA_NO_DEBUG -DARMA_USE_BLAS -DARMA_USE_LAPACK -DCAPYBARA_NCORES=@ncores@ $(SHLIB_OPENMP_CXXFLAGS) @SAFE_OPTFLAGS@
PKG_LIBS = $(SHLIB_OPENMP_CXXFLAGS) $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)
This file serves as a template for the Makevars
file.
The configure
script, which runs when the package is
installed, replaces the @ncores@
and
@SAFE_OPTFLAGS@
placeholders.
The Capybara package uses the following configure
script:
# Anticonf script by Pacha (2025)
PKG_CONFIG_NAME="capybara"
# Get R configuration
CXX=$(${R_HOME}/bin/R CMD config CXX)
CXXFLAGS=$(${R_HOME}/bin/R CMD config CXXFLAGS)
# Function to test compiler flag
test_flag() {
echo 'int main(){return 0;}' > conftest.cpp
if $CXX $CXXFLAGS $1 conftest.cpp -o conftest >/dev/null 2>&1; then
rm -f conftest conftest.cpp
return 0
else
rm -f conftest conftest.cpp
return 1
fi
}
# For CRAN, we cannot override -O2, but we can add other safe optimizations
SAFE_OPTFLAGS=""
# Test portable optimization flags that do not change the -O level
PORTABLE_OPTS="-funroll-loops -ftree-vectorize"
for opt in $PORTABLE_OPTS; do
if test_flag "$opt"; then
SAFE_OPTFLAGS="$SAFE_OPTFLAGS $opt"
fi
done
if [ -n "$SAFE_OPTFLAGS" ]; then
echo "Additional optimizations:$SAFE_OPTFLAGS"
fi
# Detect number of cores
if [ -n "$CAPYBARA_NCORES" ]; then
num_cores="$CAPYBARA_NCORES"
else
if [ -f /proc/cpuinfo ]; then
num_cores=$(grep -c "^processor" /proc/cpuinfo 2>/dev/null || echo 1)
elif [ "$(uname)" = "Darwin" ]; then
num_cores=$(sysctl -n hw.ncpu 2>/dev/null || echo 1)
else
num_cores=1
fi
if [ "$num_cores" -gt 2 ]; then
num_cores=$((num_cores - 1))
fi
fi
echo "Default thread count: $num_cores"
# Create Makevars from template
echo "Creating src/Makevars"
sed -e "s|@ncores@|${num_cores}|g" \
-e "s|@SAFE_OPTFLAGS@|${SAFE_OPTFLAGS}|g" \
src/Makevars.in > src/Makevars
echo "Configuration complete"
echo ""
echo "NOTE: This build will use the default -O2 optimization level for CRAN compliance."
echo "For maximum performance, see inst/Makevars.user.example"
exit 0
This script detects the number of cores available on the system and
sets the CAPYBARA_NCORES
variable accordingly. It also
tests for safe optimization flags that can be added to the
Makevars
file without changing the default -O2
optimization level used by R. It also serves the goal of testing that a
simple file can be compiled.