---
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.
