Add end-to-end CPU vs torch bench harness + 5-scene report (Issue #21)#10
Merged
Merged
Conversation
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.
14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an end-to-end CPU vs torch bench harness for
smallbaselineApp.pyand 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 = torchfrom PR #8,mintpy.topographicResidual.solver = torchfrom 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_topographywall)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
7c422fee71cbe5dca097b366182f20c7db6105d86890de39a0fbf71bDesign highlights
Paired cpu / torch fixtures
The first version of the harness used
docs/templates/<scene>.txtfor the cpu side andfixtures/<scene>_torch.txtfor 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
90de39aby adding pairedfixtures/<scene>_{cpu,torch}.txtfor all 5 scenes. By design the two now differ only by the 2mintpy.*.solver = torchlines — this is the syntactic invariant that makes the wall comparison fair. Verified mechanically:One scene = one shell invocation
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/*.h5pre-seedThe 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-loadedifgramStack.h5andgeometryRadar.h5. The harness pre-seeds these into both workdirs via symlink, which triggerssmallbaselineApp.py's update-mode skip onload_dataso the bench measures the full pipeline against the pre-existing stack.Two-gate regression detector
tools/compare_step_walls.pyuses 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 throughmaskTempCoh.h5) passes at 1.38e-7. The pattern points toscipy.lstsqminimum-norm vscholesky_exnear-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
bash scripts/run_end_to_end_bench.sh <scene>withFRESH=1logs_e2e_<scene>_walls_diff.jsontotals (regenerable fromlogs_e2e_<scene>_{cpu,torch}/summary.tsv)logs_e2e_<scene>_products_diff.jsondiffagainst non-solver lines is empty for every cpu/torch fixture pairdocs/templates/files were modified105d868→ 63.89×) reproduces on the repaired harness using the newFernandinaSenDT128_cpu.txtfixture