13 - Compiler Optimizations

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.



Try the cpp4r package in your browser

Any scripts or data that you put into this service are public.

cpp4r documentation built on Sept. 23, 2026, 5:06 p.m.