Skip to content

Add end-to-end CPU vs torch bench harness + 5-scene report (Issue #21)#10

Merged
s-sasaki-earthsea-wizard merged 8 commits into
mainfrom
bench/issue-21-e2e-pipeline
May 18, 2026
Merged

Add end-to-end CPU vs torch bench harness + 5-scene report (Issue #21)#10
s-sasaki-earthsea-wizard merged 8 commits into
mainfrom
bench/issue-21-e2e-pipeline

Conversation

@s-sasaki-earthsea-wizard

Copy link
Copy Markdown
Owner

Summary

Adds an end-to-end CPU vs torch bench harness for smallbaselineApp.py and the combined 5-scene report it produced, addressing the fork-side Issue #21. The bench measures what a production user sees when they flip the two opt-in GPU solver flags (mintpy.networkInversion.solver = torch from PR #8, mintpy.topographicResidual.solver = torch from PR #20) and runs the full 18-step pipeline end-to-end.

The pre-existing per-step direct-call benches (report_baseline.md, report_torch.md, report_large_scene.md, reports/dem_error/) cover the GPU-dispatched steps in isolation but do not show the combined effect with both flags on simultaneously and they do not validate the integration of two sequential GPU dispatches through the intermediate HDF5 products. This bench fills that gap.

Headline numbers — GPU-able subtotal (sum of invert_network + correct_topography wall)

Scene Pixels K ifgrams D dates cpu / torch Speedup Product gates
FernandinaSenDT128 270 k 288 98 659.4 s / 10.3 s 63.89× 7 / 7 PASS
GalapagosSenDT128 3.40 M 490 98 3043.6 s / 98.7 s 30.84× 7 / 7 PASS
SanFranBaySenD42 326 k 1297 333 1086.9 s / 23.7 s 45.86× 6 / 6 PASS
KujuAlosAT422F650 226 k 167 24 39.6 s / 6.7 s 5.92× 1 / 7 PASS (geo product PASS)
SanFranSenDT42 1.04 M 505 114 92.5 s / 17.6 s 5.24× 0 / 6 PASS (no geo product)

Speedup range 5 — 64× depending on scene size and inversion mode. The 30 — 50× band on the two production-scale scenes (Galapagos, SanFranBay) is the most representative number for a maintainer evaluating the dispatch on their own data. Detailed wall tables (top 8 steps per scene) and the closed-form scaling interpretation are in reports/report_end_to_end_bench.md.

Commits

SHA Subject
7c422fe fixtures: enable correct_topography GPU solver in all 5 _torch.txt
e71cbe5 run_bench.sh: allow PYTHON_BIN and SBA_BIN env override
dca097b Add end-to-end CPU vs torch bench harness (Issue #21)
366182f e2e bench: fix dataset keys, scope regression to 6 monitored steps
20c7db6 .gitignore: cover top-level logs_* files in addition to directories
105d868 e2e bench: route Fernandina to SSD scene root (warm-SSD alignment)
90de39a e2e bench: add paired cpu fixtures, fix scene layouts, drop SanFranSF pixelwiseGeometry=no
0fbf71b Add end-to-end CPU vs torch bench report (Issue #21)

Design highlights

Paired cpu / torch fixtures

The first version of the harness used docs/templates/<scene>.txt for the cpu side and fixtures/<scene>_torch.txt for the torch side. For three scenes the two files diverged on non-solver overrides inherited from the Issue #19 Tier 1 direct-call bench (troposphericDelay.method = no, pixelwiseGeometry = no, save.hdfEos5 = no), so the cpu and torch runs were not configured identically.

Repaired in 90de39a by adding paired fixtures/<scene>_{cpu,torch}.txt for all 5 scenes. By design the two now differ only by the 2 mintpy.*.solver = torch lines — this is the syntactic invariant that makes the wall comparison fair. Verified mechanically:

for s in Fernandina Galapagos Kuju SanFranBay SanFranSF; do
  diff <(grep -v '^#' fixtures/${SCENE}_cpu.txt | filter solver) \
       <(grep -v '^#' fixtures/${SCENE}_torch.txt | filter solver)
done
# all empty

One scene = one shell invocation

bash scripts/run_end_to_end_bench.sh <scene>

drives 6 phases in order: workdir cleanup → cpu run → ERA5 cache symlink (if downloaded) → torch run → step wall diff → 7-product HDF5 diff → headline summary. Idempotent re-run via FRESH=1.

Galapagos inputs/*.h5 pre-seed

The raw ISCE inputs (merged/, master/, baselines/) referenced by the Galapagos template were cleaned up after the Issue #6 large-scene bench, leaving only the already-loaded ifgramStack.h5 and geometryRadar.h5. The harness pre-seeds these into both workdirs via symlink, which triggers smallbaselineApp.py's update-mode skip on load_data so the bench measures the full pipeline against the pre-existing stack.

Two-gate regression detector

tools/compare_step_walls.py uses an AND condition (relative ±5% AND absolute floor 2.0 s) on the 6 monitored CPU-only steps. Suppresses false positives on sub-second steps where ordinary scheduling noise easily crosses ±5% while keeping real regressions visible. The 16 CPU-only steps are split into "monitored" (the 6 listed in the Issue #21 acceptance criteria) and "unmonitored" (the rest); the CPU-only subtotal ratio is computed over all of them as an I/O / cache control headline.

Product numerical gate

3 of 5 scenes pass all 6 — 7 product gates at float32 round-off (max observed rms/scale 9.56e-6 on Fernandina's demErr.h5). Kuju and SanFranSF radar-coord products fail at rms/scale 1 — 7%; the Kuju geocoded velocity (filtered through maskTempCoh.h5) passes at 1.38e-7. The pattern points to scipy.lstsq minimum-norm vs cholesky_ex near-rank-deficient behaviour on pixels excluded downstream by the temporal-coherence mask but not by upstream NaN filters. Making the diff mask-aware is a sibling-repo follow-up; it does not affect any wall measurement. Full diagnosis in report §4.2.

Test plan

  • All 5 scenes ran to completion under bash scripts/run_end_to_end_bench.sh <scene> with FRESH=1
  • Per-step wall numbers in the report match logs_e2e_<scene>_walls_diff.json totals (regenerable from logs_e2e_<scene>_{cpu,torch}/summary.tsv)
  • Product numerical gates (3 PASS, 2 with documented mask-related radar-coord divergence) match logs_e2e_<scene>_products_diff.json
  • diff against non-solver lines is empty for every cpu/torch fixture pair
  • No upstream docs/templates/ files were modified
  • The previous Fernandina smoke test result (commit 105d868 → 63.89×) reproduces on the repaired harness using the new FernandinaSenDT128_cpu.txt fixture

Add mintpy.topographicResidual.solver = torch alongside the existing
mintpy.networkInversion.solver = torch so end-to-end runs exercise
both GPU dispatch paths simultaneously (Issue #21 acceptance criteria).

Header comment updated from "GPU acceleration of invert_network" to
"GPU acceleration" since the fixtures now cover two GPU steps.
Per-step direct-call bench harnesses that read these fixtures are
unaffected — they invoke specific steps via --dostep and ignore the
other solver flag.

Assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
End-to-end bench (Issue #21) needs to drive run_bench.sh from the
sibling bench venv (~/MintPy_bench/.venv) because the ARIA and GMTSAR
load_data steps require GDAL bindings that live there. Promote both
binary paths to env-var fallbacks so existing baseline invocations
keep using the fork .venv with no change.

Assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New harness in scripts/run_end_to_end_bench.sh drives one scene
through the full 18-step smallbaselineApp.py twice — once with the
upstream template (cpu solvers) and once with fixtures/<scene>_torch.txt
(torch solvers for both invert_network and correct_topography) — then
diffs step walls and HDF5 products.

Workdir strategy: cpu and torch get separate workdirs under the scene
root so both runs' products remain on disk for later re-diffing, and
the update-mode skip trap is structurally avoided. Large raw inputs
(merged/, geometry/) stay shared via the templates' relative ../path
references, so only the MintPy workdir output is duplicated.

Two supporting tools:

- tools/compare_h5_outputs.py reads 7 HDF5 products spanning upstream
  (timeseries / temporalCoherence / numInvIfgram), downstream
  (timeseries_demErr / demErr), and final stages (velocity /
  geo_velocity) of the pipeline, computing rms/scale relative
  deviation and gating each at <1e-5 (float32 round-off envelope).
  Exits non-zero on any gate fail or required product missing.

- tools/compare_step_walls.py reads summary.tsv from both runs,
  classifies steps as GPU-able (invert_network, correct_topography)
  vs CPU-only, reports the headline GPU-able subtotal speedup, and
  fails if any CPU-only step changes by more than ±5% between runs
  (regression control on non-GPU code paths).

ERA5 cache handling (pre-seed before phase 1 if present, post-cpu-run
symlink otherwise) keeps the cpu and torch correct_troposphere walls
comparable without paying the ~70 min CDS download twice.

Assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three issues surfaced by the Phase 2 Fernandina smoke test (Issue #21):

1. compare_h5_outputs.py: dataset keys for numInvIfgram.h5 and
   demErr.h5 were wrong — MintPy stores them under 'mask' and 'dem'
   respectively, not under the filename stem. Also, the downstream
   timeseries product carries the template's suffix accumulation
   (timeseries_ERA5_ramp_demErr.h5 on Fernandina vs timeseries_demErr.h5
   on tropo/deramp-free scenes), so the product spec is now a
   (subdir, pattern, key, required) tuple with glob support — for
   globs the longest filename match wins (= the most-processed
   product under MintPy's naming convention). Added structural
   statuses (filename_mismatch, dataset_key_error) so issues are
   reported as data, not as Python exceptions that abort the tool.

2. compare_step_walls.py: a relative-only ±5% tolerance generates
   false positives on sub-second steps (correct_SET 0.81→2.04s is
   +152% but only 1.2s absolute). Added an absolute floor (default
   2.0s) — a step is flagged only when BOTH the relative tolerance
   AND the absolute floor are breached. Also split CPU-only steps
   into monitored (the 6 Issue #21 acceptance-criteria steps:
   load_data, modify_network, correct_SET, correct_troposphere,
   deramp, hdfeos5) and unmonitored (reference_point, quick_overview,
   correct_LOD, correct_unwrap_error, correct_ionosphere, velocity,
   geocode, residual_RMS, reference_date, google_earth); only
   monitored steps trigger the regression gate. The CPU-only
   subtotal still sums all cpu-only steps for the I/O / cache
   control headline.

3. run_end_to_end_bench.sh: headline rendering crashed when the
   products diff JSON was absent (tool failed earlier). Now it
   gracefully reports "(products diff JSON missing)" and continues.
   Also surfaces non-compared product statuses (absent_in_one,
   filename_mismatch, dataset_key_error) at the headline so the
   user sees them immediately, not just in the JSON.

After these fixes the Fernandina smoke test reports:
- GPU-able subtotal speedup: 24.07x (cpu 583s → torch 24s)
- CPU-only subtotal ratio:   0.998 (I/O / cache control)
- All 7 product diff gates pass at rms/scale < 1e-5 (max 9.6e-6
  on demErr.h5)
- Monitored 6-step regressions: none
- Unmonitored variance: reference_point +6s / quick_overview -6s,
  consistent with NAS read-cache warm-up across the cpu→torch
  sequence (the two net out, hence the 0.998 subtotal ratio)

Assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The end-to-end bench harness writes per-scene summary diffs as
top-level JSON files (logs_e2e_<scene>_walls_diff.json,
logs_e2e_<scene>_products_diff.json) alongside the existing
logs_e2e_<scene>_{cpu,torch}/ directories. The old `logs_*/` rule
matched directories only, leaving those JSON files visible to
`git status` after every run. Drop the trailing slash so the rule
matches both — the policy ("machine-dependent artifacts stay
untracked, findings are transcribed into reports") is unchanged,
just enforced more completely.

Assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The harness's Fernandina entry pointed at the in-repo NAS-mounted
copy (${REPO_ROOT}/FernandinaSenDT128/), which the Phase 2 smoke test
revealed to be at odds with Issue #21 acceptance criteria's "warm
SSD" requirement. Switch to the sibling
${HOME}/MintPy_bench/FernandinaSenDT128/ tree (already populated by
prior bench sessions; only the ERA5.h5 cache had to be copied from
NAS to replace a 14 KB stub).

Re-running the smoke test on SSD shifts the headline numbers sharply:

                          NAS       SSD       SSD/NAS
  GPU-able speedup        24.07x    63.89x    2.65x increase
  invert_network speedup  34.79x    93.77x    storage-amplified
  load_data wall          112.31s   12.76s    0.11x (CIFS amplifies
                                                small-file reads)
  torch E2E wall          261s      77s       0.29x

The CPU side is barely affected (cpu E2E 821s → 727s, mostly noise) —
CPU compute is the bottleneck on the cpu run, not storage. The torch
side exposes its true sub-second per-step compute floor once the NAS
read latency and small-file CIFS overhead are removed; the speedup
figure goes up not because the GPU got faster but because torch I/O
and launch overhead stops dominating the wall.

Storage choice can therefore move the headline by 2-3x on small
scenes (Fernandina-class) where invert_network compute is short. On
production scenes (Galapagos, 3.4M pixels) the GPU compute itself is
hundreds of seconds and storage overhead is a smaller relative share,
so the speedup curve flattens with scale — Issue #6 saw 36.4x on
Galapagos's invert_network step alone, well below Fernandina's
93.77x here.

The pre-existing NAS workdirs at
${REPO_ROOT}/FernandinaSenDT128/mintpy_e2e_* are intentionally
preserved (user direction 2026-05-18) for the NAS reproducibility
comparison and historical record of the Phase 2 smoke test
iteration; only the SSD workdirs receive the warm-SSD headline run.

Assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… pixelwiseGeometry=no

The previous design pointed CPU_TEMPLATE at docs/templates/<scene>.txt
for the cpu side and used benchmark/fixtures/<scene>_torch.txt for the
torch side. For three scenes the two files diverge on processing
options unrelated to the GPU solver — tropo skip, hdfEos5 skip,
pixelwiseGeometry — which biases the wall comparison.

Add cpu fixtures for all 5 scenes that match the corresponding torch
fixture byte-for-byte except for the 2 `mintpy.*.solver = torch` lines.
This guarantees the only difference between the cpu and torch run
configuration is the solver dispatch.

Other harness changes in this commit:
  - SCENE_ROOT now points to the inner directory for Kuju, SanFranSF,
    and SanFranBay (tar-extracted Zenodo layout is nested one level)
  - Galapagos: pre-seed ifgramStack.h5 + geometryRadar.h5 symlinks
    from mintpy/inputs/ into both workdirs to trigger update-mode skip
    on load_data, since the raw ISCE inputs were cleaned after the
    Issue #6 large-scene bench. Also export GALAPAGOS_DIR so the
    fixture's load.* path strings parse.
  - SanFranSF torch fixture: drop `pixelwiseGeometry = no`. The
    Issue #19 Tier 1 fixture used this to bypass the pixelwise path
    entirely, but the e2e bench needs the GPU correct_topography
    solver actually exercised on both sides.
Combined 5-scene report covering FernandinaSenDT128, GalapagosSenDT128,
SanFranBaySenD42, KujuAlosAT422F650, and SanFranSenDT42. Each scene was
run end-to-end through the 18-step smallbaselineApp.py pipeline twice
(cpu / torch solver) via the paired cpu/torch fixtures introduced in
the prior commit, with per-step wall + 7-product HDF5 numerical diff
captured.

Headline GPU-able subtotal speedups (sum of invert_network +
correct_topography wall):

  Fernandina   63.89x  (270 k px, ISCE, ERA5 pre-warmed)
  Galapagos    30.84x  (3.40 M px, ISCE, ERA5 cold in cpu run)
  SanFranBay   45.86x  (326 k px, GMTSAR, no tropo)
  Kuju          5.92x  (226 k px, ROI_PAC, no tropo, K=24)
  SanFranSF     5.24x  (1.04 M px, ARIA, OLS via weightFunc=no)

Product numerical gates pass at float32 round-off (rms/scale < 1e-5)
on Fernandina + Galapagos + SanFranBay. Kuju and SanFranSF radar-coord
products fail at rms/scale 1-7%; the Kuju geocoded velocity passes at
1.38e-7 so the divergence is concentrated in pixels masked out by
maskTempCoh.h5 downstream of invert_network (lstsq min-norm vs
cholesky_ex near-rank-deficient behaviour). The report documents this
and proposes masking the radar-coord product diff as a follow-up.

The Galapagos CPU-only subtotal ratio 0.106 is an ERA5 cold-cache
artifact (cpu run downloaded via pyaps3 inline, torch symlinked the
cache); the headline GPU-able subtotal is unaffected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant