From 118df77678cbe45acaa336c072ddcb5ffd65a7ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Jul 2026 20:35:16 +0000 Subject: [PATCH 1/6] Initial plan From 6a4e8c25ff3f63a80316e7896c8403f9e68ee242 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Jul 2026 20:37:32 +0000 Subject: [PATCH 2/6] feat: scaffold FIMS memory benchmark repository layout --- .devcontainer/devcontainer.json | 4 +++ .devcontainer/postCreate.sh | 19 ++++++++++++ R/main.R | 12 ++++++++ R/run_benchmark.R | 31 +++++++++++++++++++ R/setup_FIMS | 20 ++++++++++++ README.md | 54 ++++++++++++++++++++++++++++++++- outputs/.gitkeep | 0 scripts/run_massif.sh | 23 ++++++++++++++ 8 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/postCreate.sh create mode 100644 R/main.R create mode 100644 R/run_benchmark.R create mode 100644 R/setup_FIMS create mode 100644 outputs/.gitkeep create mode 100755 scripts/run_massif.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..4104cb5 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,4 @@ +{ + "name": "FIMS Memory Benchmarks", + "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..6ff4ae7 --- /dev/null +++ b/R/run_benchmark.R @@ -0,0 +1,31 @@ +if (!requireNamespace("TMB", quietly = TRUE)) { + stop("Package 'TMB' is required for benchmark stages.") +} + +cat("Stage 1: Static model construction through MakeADFun()\n") +if (exists("fims_stage1_builder", mode = "function")) { + obj <- fims_stage1_builder() +} else { + stop( + paste( + "Define function `fims_stage1_builder()` before sourcing R/run_benchmark.R.", + "It must construct the model and return the object from MakeADFun()." + ) + ) +} + +cat("Stage 2: Single evaluation calls (obj$fn() and obj$gr())\n") +obj$fn() +obj$gr() + +cat("Stage 3: Full model run with nlminb (without sdreport)\n") +opt <- nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr) + +cat("Stage 4: Full model run with nlminb and sdreport\n") +obj$env$last.par.best <- opt$par +TMB::sdreport(obj) + +cat("Stage 5: Cleanup and retention check (FreeADFun + gc)\n") +TMB::FreeADFun(obj) +rm(obj, opt) +invisible(gc()) 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" From f8452dd7f5d23ba2ed8f66af5c0187de091f20e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Jul 2026 20:49:19 +0000 Subject: [PATCH 3/6] refactor: rename setup script to install_FIMS_debug --- R/{setup_FIMS => install_FIMS_debug} | 0 R/main.R | 2 +- README.md | 2 +- scripts/run_massif.sh | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename R/{setup_FIMS => install_FIMS_debug} (100%) diff --git a/R/setup_FIMS b/R/install_FIMS_debug similarity index 100% rename from R/setup_FIMS rename to R/install_FIMS_debug diff --git a/R/main.R b/R/main.R index e469b87..6b35ce6 100644 --- a/R/main.R +++ b/R/main.R @@ -1,4 +1,4 @@ -source("R/setup_FIMS") +source("R/install_FIMS_debug") run_branch_benchmark <- function(ref) { install_fims_debug(ref) diff --git a/README.md b/README.md index 7d2addf..fa67599 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ These settings preserve symbols and frame pointers for profiler-friendly builds. ## Install FIMS in Debug Mode -`R/setup_FIMS` provides: +`R/install_FIMS_debug` provides: ```r install_fims_debug(ref = "main") diff --git a/scripts/run_massif.sh b/scripts/run_massif.sh index 7a7c046..77aceb8 100755 --- a/scripts/run_massif.sh +++ b/scripts/run_massif.sh @@ -12,7 +12,7 @@ run_ref() { valgrind --tool=massif \ --massif-out-file="$out_file" \ - Rscript -e "source('R/setup_FIMS'); install_fims_debug('$ref'); source('R/run_benchmark.R')" + Rscript -e "source('R/install_FIMS_debug'); install_fims_debug('$ref'); source('R/run_benchmark.R')" } run_ref "main" "$OUTPUT_DIR/massif_main.out" From 5696f79eec1fe37021da8c58ffa08aa69e51fbda Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Jul 2026 20:58:29 +0000 Subject: [PATCH 4/6] chore: clear run_benchmark script content --- R/run_benchmark.R | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/R/run_benchmark.R b/R/run_benchmark.R index 6ff4ae7..e69de29 100644 --- a/R/run_benchmark.R +++ b/R/run_benchmark.R @@ -1,31 +0,0 @@ -if (!requireNamespace("TMB", quietly = TRUE)) { - stop("Package 'TMB' is required for benchmark stages.") -} - -cat("Stage 1: Static model construction through MakeADFun()\n") -if (exists("fims_stage1_builder", mode = "function")) { - obj <- fims_stage1_builder() -} else { - stop( - paste( - "Define function `fims_stage1_builder()` before sourcing R/run_benchmark.R.", - "It must construct the model and return the object from MakeADFun()." - ) - ) -} - -cat("Stage 2: Single evaluation calls (obj$fn() and obj$gr())\n") -obj$fn() -obj$gr() - -cat("Stage 3: Full model run with nlminb (without sdreport)\n") -opt <- nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr) - -cat("Stage 4: Full model run with nlminb and sdreport\n") -obj$env$last.par.best <- opt$par -TMB::sdreport(obj) - -cat("Stage 5: Cleanup and retention check (FreeADFun + gc)\n") -TMB::FreeADFun(obj) -rm(obj, opt) -invisible(gc()) From df6134d5441130f8560d0fb1c877bb44918b125f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Jul 2026 21:02:26 +0000 Subject: [PATCH 5/6] refactor: rename install script back to setup_FIMS --- R/main.R | 2 +- R/{install_FIMS_debug => setup_FIMS} | 0 README.md | 2 +- scripts/run_massif.sh | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename R/{install_FIMS_debug => setup_FIMS} (100%) diff --git a/R/main.R b/R/main.R index 6b35ce6..e469b87 100644 --- a/R/main.R +++ b/R/main.R @@ -1,4 +1,4 @@ -source("R/install_FIMS_debug") +source("R/setup_FIMS") run_branch_benchmark <- function(ref) { install_fims_debug(ref) diff --git a/R/install_FIMS_debug b/R/setup_FIMS similarity index 100% rename from R/install_FIMS_debug rename to R/setup_FIMS diff --git a/README.md b/README.md index fa67599..7d2addf 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ These settings preserve symbols and frame pointers for profiler-friendly builds. ## Install FIMS in Debug Mode -`R/install_FIMS_debug` provides: +`R/setup_FIMS` provides: ```r install_fims_debug(ref = "main") diff --git a/scripts/run_massif.sh b/scripts/run_massif.sh index 77aceb8..7a7c046 100755 --- a/scripts/run_massif.sh +++ b/scripts/run_massif.sh @@ -12,7 +12,7 @@ run_ref() { valgrind --tool=massif \ --massif-out-file="$out_file" \ - Rscript -e "source('R/install_FIMS_debug'); install_fims_debug('$ref'); source('R/run_benchmark.R')" + Rscript -e "source('R/setup_FIMS'); install_fims_debug('$ref'); source('R/run_benchmark.R')" } run_ref "main" "$OUTPUT_DIR/massif_main.out" From 3248f957d9ba2277b632a8f0aaee2a1e4b983a4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Jul 2026 21:06:38 +0000 Subject: [PATCH 6/6] Update devcontainer to install required packages --- .devcontainer/devcontainer.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4104cb5..9a1cb41 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,4 +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" }