-
Notifications
You must be signed in to change notification settings - Fork 0
GPU Speedup Scaling Model
Reference page for the empirical observation across correct_topography
and invert_network bench scenes that speedup tracks K (= number of
valid pixels to invert) much more strongly than D (= num_date) or P (=
per-pixel design matrix width). This page works out the structural
reason from the per-pixel solve cost up, fits coefficients against
measured wall times, and gives a quick "expected speedup for a new
scene" lookup.
Empirical data behind this page:
-
mintpy-benchmark/reports/dem_error/report_bench_survey.md—correct_topography5-scene survey (Issue #19 Tier 1) -
reports/report_large_scene.md—invert_networkGalapagos -
reports/dem_error/report_fernandina.md/report_galapagos.md— per-scene context
Algorithm-side discussion of why the per-pixel solver choice matters at all is in gels QR vs. Normal Equation + Cholesky.
K is the batch dimension — GPU parallelism amortises the per-pixel
Python + scipy dispatch overhead that CPU pays K times in a for loop.
D and P are per-solve internal dimensions — they appear in both
walls with similar prefactors and don't move the speedup ratio much.
The speedup-vs-scene model is
with three regimes:
| Regime | Condition | σ behaviour | Example |
|---|---|---|---|
| (A) Overhead-bound | K small, |
|
Tier 1 small-K cluster: σ = 2.4–2.6× regardless of D ∈ [24, 114] |
| (B) Transition | K small, D large enough that |
K cancels; σ depends on D, P, prefactor ratios; can decrease in D | SanFranBay (K=326k, D=333) drops to 1.76× |
| (C) Compute-bound | K large, |
correct_topography) |
Galapagos (K = 3.4M): σ = 6.15×, approaching asymptote |
| Symbol | Meaning | Tier 1 + Galapagos range |
|---|---|---|
Number of valid pixels to invert (image_shape × valid_mask count) |
226k – 3.4M | |
num_date — length of the per-pixel time series |
24 – 333 | |
Per-pixel design matrix width. For correct_topography: invert_network: |
4 – 6 (correct_topography) |
For each valid pixel both CPU and GPU paths solve
then back-solve ts_cor / ts_res outputs.
Per-pixel flop count:
| step | flops |
|---|---|
|
|
|
| Cholesky on |
|
| Triangular solves | |
ts_cor / ts_res reconstruction |
|
For the Tier 1 ranges (
CPU iterates over the K pixels in Python; each pixel calls
scipy.linalg.lstsq:
-
$\alpha \approx 10\text{–}40,\mu\text{s}$ /pixel — D-independent Python + scipy dispatch overhead. The Pythonforloop pays this per iteration regardless of D. -
$\gamma \approx 0.1\text{–}1,\text{ns}$ /flop — single-core scalar compute on the dominant$D P^2$ flop count.
The relative size of
GPU does one batched solve across all K pixels simultaneously:
-
$F \approx 3,\text{s}$ — Framework fixed cost: CUDA init,timeseries.h5+geometryGeo.h5reads, mask building, kernel launches. K-independent. -
$\beta \sim \mu\text{s}$ /pixel — CPU-side bookkeeping (mask application, index construction) before the GPU dispatch. -
$\delta \sim \text{ns}$ /(D,P)-element — memory traffic: the(K, D, P)G_batchtensor is built on CPU via numpy, transferred via PCIe (Gen4 ~32 GB/s host→device), then read by GPU kernels; outputs(K, D)ts_cor/ts_resflow back. -
$\varepsilon \approx 10^{-12},\text{s/flop}$ — GPU peak compute. The$\varepsilon K D P^2$ term is sub-second for every scene measured; never the dominant cost on the GPU side.
Crucially: D enters the GPU wall dominantly through (K, D, P) tensor at K = 326k, D = 333 is already 1.65 GiB at float32
— moving that data dominates seconds-scale wall time, not the 60–100 ms
of actual matmul work.
The Python for loop on CPU pays the interpreter cost
D and P, by contrast, are inside the per-pixel solve:
- A larger D means each per-pixel
lstsqcall processes more data, on both sides. CPU pays$\gamma D P^2$ per pixel; GPU pays$\delta D P$ per pixel (memory) +$\varepsilon D P^2$ per pixel (compute, hidden). D appears in both with similar coefficients → speedup ratio is roughly D-invariant. - A larger P means more columns of the per-pixel design matrix. CPU
flops scale
$P^2$ ; GPU memory scales$P$ (and compute$P^2$ ). P is the only way to get the GPU compute term to dominate over its memory term, but for the practical range$P \in [4, 10]$ this never happens at small K because$\varepsilon$ stays sub-second.
The clean way to see this: in the K-cancelled limit (regime B / C),
K disappears. The GPU advantage that scales with K — the
- implementation.
Conditions:
D disappears. Speedup scales only with K. This is the cluster Kuju / SanFranSF / Fernandina(SSD) sit in: σ ≈ 2.4–2.6× regardless of D ∈ [24, 114] or P ∈ [4, 6].
Conditions:
K cancels and the formula collapses to
This is SanFranBay (K = 326k, D = 333, P = 4): σ drops to 1.76×.
Conditions:
Same K-cancelled formula as regime (B):
But now the
— independent of D, linear in P, and bounded by the per-flop CPU
vs per-byte GPU coefficient ratio. For correct_topography at P = 4
on RTX 5080 this evaluates to ≈ 10× (report_galapagos.md §2).
Galapagos (K = 3.4M, P = 4) at σ = 6.15× is within striking distance of this asymptote.
correct_topography, fork commit
60c9e3ac,
local SSD, RTX 5080 / PyTorch 2.11+cu128:
| Scene | K | D | P |
|
|
σ | Regime |
|---|---|---|---|---|---|---|---|
| Kuju (ALOS / ROI_PAC) | 226k | 24 | 4 | 9.14 | 3.61 | 2.53× | (A) |
| SanFranSF (ARIA) | 260k | 114 | 4 | 9.32 | 3.84 | 2.43× | (A) |
| Fernandina (ISCE2, SSD) | 270k | 98 | 6 | 11.15 | 4.36 | 2.56× | (A) |
| SanFranBay (GMTSAR) | 326k | 333 | 4 | 14.53 | 8.23 | 1.76× | (B) |
| Galapagos (ISCE2) | 3,400k | 98 | 4 | 163.83 | 26.66 | 6.15× | (C) |
Approximate coefficient fit (single-significant-figure, ±30%):
-
$\alpha \approx 40,\mu\text{s}$ /pixel — from Kuju:$T_{\text{cpu}}/K \approx 9.14,\text{s} / 226\text{k} \approx 40,\mu\text{s}$ at small D where the$\gamma D P^2$ term is negligible. -
$F \approx 3,\text{s}$ — from Kuju:$T_{\text{gpu}}$ at smallest scene is 3.6 s, mostly framework overhead. -
$\delta \approx 12,\text{ns}$ /element — from SanFranBay: ($T_{\text{gpu}} - F$ ) / (K D P) ≈ 5.2 s / 4.3e8 ≈ 12 ns. -
$\gamma \approx 0.3,\text{ns}$ /flop — from Galapagos:$T_{\text{cpu}} / (K D P^2) \approx 164$ s / 5.3e9 ≈ 0.3 ns.
Cross-check by plugging back into the σ formula:
| Scene | Predicted σ | Measured σ |
|---|---|---|
| Kuju |
|
2.53× |
| SanFranSF |
|
2.43× |
| SanFranBay |
|
1.76× |
| Galapagos |
|
6.15× |
Qualitative story holds; absolute prefactor estimates are ±30%
slop-level (real
correct_topography on RTX 5080 / SSD, ROM-style estimates:
| K | Expected σ | Note |
|---|---|---|
| < 100k | ≲ 2× | K too small to amortise |
| 100k – 300k | 2 – 2.5× | Regime (A); D and P don't matter much in this range |
| 300k – 1M | 2.5 – ≈ 4× | K-axis under-sampled by current data; first crossing into regime (B)/(C); a Tier 2 scene would pin this |
| 1M – 10M | 4 – ≈ 10× | GPU clearly worthwhile, approaching the |
| > 10M | ≈ 10× | Diminishing returns past Galapagos scale |
The GPU path is more I/O sensitive than the CPU path because its
compute portion is sub-second; the timeseries.h5 read becomes the
dominant share of
| Storage |
|
|
σ |
|---|---|---|---|
| local SSD | 11.15 | 4.36 | 2.56× |
| CIFS / NAS | 7.45 | 7.64 | 0.97× |
Same scene, same hardware. NAS storage erases the GPU win at K = 270k. Prefer SSD for the GPU path whenever practical.
Both steps share the same K-as-batch-axis structure. The asymptote difference comes from the per-pixel flop count:
correct_topography (Galapagos) |
invert_network (Galapagos) |
|
|---|---|---|
| Design matrix | ||
| Per-pixel flops | ||
| Ratio | 1× | ~2,800× more per-pixel work |
In the regime-(C) asymptote invert_network lifts the asymptote ceiling by roughly invert_network Galapagos σ = 36.4× wall
(44.4× internal) (report_large_scene.md)
is consistent with this lift.
This is also why the algorithm-side optimisation (gels → batched
Cholesky in gels QR vs. Normal Equation + Cholesky)
mattered so much for invert_network at K = 3.4M and matters comparatively
less for correct_topography: per-pixel work for invert_network is
large enough to expose the QR launch-overhead pathology, while
correct_topography's smaller per-pixel work hides launch overhead
inside framework overhead anyway.
Apply the same decomposition:
-
Count per-pixel flops of the dominant kernel (
$D P^2$ for normal-equation Cholesky; different for other algorithms). - Identify the dominant memory-traffic term on GPU: typically the shape of the largest per-pixel intermediate that has to flow through PCIe + GPU memory hierarchy.
-
Estimate the per-pixel CPU interpreter overhead
$\alpha$ : usually in the tens-of-µs range when calling scipy or numpy from a Python for-loop. -
Find the asymptote
$\sigma_\infty \approx \gamma \times (\text{per-pixel-flops}) / \delta \times (\text{per-pixel-bytes})$ using the hardware coefficients ($\gamma \approx 0.3,\text{ns}$ /flop,$\delta \approx 12,\text{ns}$ /element on the system measured here). -
Find the K crossover where
$K \alpha / F \approx 1$ : this is the K at which speedup starts climbing out of regime (A). For our system$F \approx 3,\text{s}$ ,$\alpha \approx 40,\mu\text{s}$ , crossover at$K \approx 75\text{k}$ . Below this, GPU dispatch is overhead-bound regardless of D or P. -
Verify the asymptote is much higher than the crossover-region
speedup (
$\approx 2$ × in our case). If not, GPU dispatch won't pay off at any practical K, and the step is not worth GPU-porting.
-
gels QR vs. Normal Equation + Cholesky — algorithm choice for the per-pixel solver (the
$\gamma$ -coefficient discussion in detail) - Performance Benchmarks — index of all measured bench reports with commit-pinned permalinks
- GPU Quick Start — end-to-end setup recipe to reproduce the bench results