diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..9a1cb41 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,15 @@ +{ + "name": "FIMS Memory Benchmarks", + "image": "ghcr.io/rocker-org/devcontainer/r-ver:4.3", + "features": { + "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { + "packages": "g++,gcc,libelf-dev,libunwind-dev,libcap2-bin,make,gdb,valgrind", + "updatePackages": true + }, + "ghcr.io/rocker-org/devcontainer-features/r-packages:1": { + "packages": "covr,dplyr,devtools,ggplot2,graphics,methods,Rcpp,RcppEigen,scales,snowfall,TMB,tibble,tidyr,usethis", + "installSystemRequirements": true + } + }, + "postCreateCommand": "bash .devcontainer/postCreate.sh" +} diff --git a/.devcontainer/postCreate.sh b/.devcontainer/postCreate.sh new file mode 100755 index 0000000..3922fa5 --- /dev/null +++ b/.devcontainer/postCreate.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +mkdir -p "$HOME/.R" +MAKEVARS="$HOME/.R/Makevars" + +touch "$MAKEVARS" + +add_line_if_missing() { + local line="$1" + if ! grep -Fqx "$line" "$MAKEVARS"; then + printf '%s\n' "$line" >> "$MAKEVARS" + fi +} + +add_line_if_missing "PKG_CXXFLAGS += -g -O0 -fno-omit-frame-pointer -fvisibility=default" +add_line_if_missing "PKG_STRIP = true" + +echo "Configured $MAKEVARS for debug builds." diff --git a/R/main.R b/R/main.R new file mode 100644 index 0000000..e469b87 --- /dev/null +++ b/R/main.R @@ -0,0 +1,12 @@ +source("R/setup_FIMS") + +run_branch_benchmark <- function(ref) { + install_fims_debug(ref) + source("R/run_benchmark.R") +} + +# 1. Benchmark Main +run_branch_benchmark("main") # Outputs to outputs/massif_main.out + +# 2. Benchmark Feature Branch +run_branch_benchmark("xptr-refactor") # Outputs to outputs/massif_xptr.out diff --git a/R/run_benchmark.R b/R/run_benchmark.R new file mode 100644 index 0000000..e69de29 diff --git a/R/setup_FIMS b/R/setup_FIMS new file mode 100644 index 0000000..9b5794a --- /dev/null +++ b/R/setup_FIMS @@ -0,0 +1,20 @@ +#' Install a specific FIMS branch compiled in Debug Mode +#' +#' @param ref Branch name, tag, or commit hash (e.g. "main", "xptr-refactor") +install_fims_debug <- function(ref = "main") { + message(sprintf("Installing NOAA-FIMS/FIMS@%s in debug mode...", ref)) + + # Ensure remotes is available + if (!requireNamespace("remotes", quietly = TRUE)) { + install.packages("remotes") + } + + # Force compilation from source with user's ~/.R/Makevars applied + remotes::install_github( + repo = "NOAA-FIMS/FIMS", + ref = ref, + force = TRUE, + build_vignettes = FALSE, + INSTALL_opts = c("--no-multiarch") + ) +} diff --git a/README.md b/README.md index cf3205a..7d2addf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,54 @@ # FIMSMemoryBenchmarks -A repo to compare and keep track of FIMS memory footprint + +A repository to benchmark and compare the memory footprint of NOAA-FIMS/FIMS builds. + +## Repository Layout + +- `/R`: Helper R scripts for setup and benchmark stage execution. +- `/scripts`: Shell runners for memory profiling tools. +- `/outputs`: Benchmark and profiler outputs (`.gitkeep` included). +- `/.devcontainer`: Codespaces setup for debug-safe R compilation flags. + +## Debug Build Configuration in Codespaces + +This repository includes `.devcontainer/postCreate.sh`, which configures `~/.R/Makevars` with: + +- `PKG_CXXFLAGS += -g -O0 -fno-omit-frame-pointer -fvisibility=default` +- `PKG_STRIP = true` + +These settings preserve symbols and frame pointers for profiler-friendly builds. + +## Install FIMS in Debug Mode + +`R/setup_FIMS` provides: + +```r +install_fims_debug(ref = "main") +``` + +It installs `NOAA-FIMS/FIMS` from GitHub for a chosen branch/tag/commit using source compilation. + +## Benchmark Stages + +`R/run_benchmark.R` is structured into five stages: + +1. Static model construction through `MakeADFun()` +2. Single evaluation calls (`obj$fn()` and `obj$gr()`) +3. Full optimization run with `nlminb` (without `sdreport`) +4. Full optimization run with `nlminb` and `sdreport` +5. Cleanup and retention check via `TMB::FreeADFun(obj)` and `gc()` + +> Before sourcing `R/run_benchmark.R`, define `fims_stage1_builder()` so it returns the stage-1 `MakeADFun()` object. + +## Run Massif Benchmarks + +```bash +bash scripts/run_massif.sh +``` + +This generates: + +- `outputs/massif_main.out` +- `outputs/massif_xptr.out` + +Use `ms_print` to inspect each output file. diff --git a/outputs/.gitkeep b/outputs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/run_massif.sh b/scripts/run_massif.sh new file mode 100755 index 0000000..7a7c046 --- /dev/null +++ b/scripts/run_massif.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +OUTPUT_DIR="$REPO_ROOT/outputs" + +mkdir -p "$OUTPUT_DIR" + +run_ref() { + local ref="$1" + local out_file="$2" + + valgrind --tool=massif \ + --massif-out-file="$out_file" \ + Rscript -e "source('R/setup_FIMS'); install_fims_debug('$ref'); source('R/run_benchmark.R')" +} + +run_ref "main" "$OUTPUT_DIR/massif_main.out" +run_ref "xptr-refactor" "$OUTPUT_DIR/massif_xptr.out" + +echo "Massif outputs written to $OUTPUT_DIR" +echo " - $OUTPUT_DIR/massif_main.out" +echo " - $OUTPUT_DIR/massif_xptr.out"