--- title: "13 - Compiler Optimizations" output: litedown::html_format: options: toc: true number_sections: true vignette: > %\VignetteIndexEntry{13 - Compiler Optimizations} %\VignetteEngine{litedown::vignette} %\VignetteEncoding{UTF-8} editor: markdown: wrap: sentence bibliography: ["references.bib"] --- ## 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): ```bash # 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](https://github.com/pachadotdev/capybara) package has the following `Makevars.in` file: ```make 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: ```bash # 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.