SNAP 14.0.0 reads the Copernicus DEM several times slower than SNAP 13.0.0, and the
cost is large enough to double the wall-clock time of a full Sentinel-1 InSAR chain.
I see two defects in the new CopernicusDirectElevationTile that can explain it.
My chain :
Sentinel-1 IW pair, two sub-swaths: Apply-Orbit → TOPSAR-Split → Back-Geocoding → ESD → Interferogram → TOPSAR-Merge → TopoPhaseRemoval → Multilook → Goldstein → snaphu → Terrain-Correction.
My env :
SNAP 14.0.0 and 13.0.0 installed side by side on the same machine (Ubuntu 22.04 / WSL2), bundled JRE 21.0.6, -Xmx43G in both, identical snap.properties and bin/gpt, shared ~/.snap/auxdata with the Copernicus tiles already downloaded, nothing is fetched during the runs. Also reproduced on Windows 10 with the SNAP 14 GUI.
Result : 16 min with SNAP 13, 41 min with SNAP 14, same graphs, same products, runs one after the other on an idle machine. snaphu, which is external to SNAP, takes the same time in both.
Minimal reproduction :
Minimal reproduction, about one minute: Read → AddElevation → Write on the same
product (2186 × 2215), only demName changes.
| DEM | SNAP 13 | SNAP 14 |
|---|---|---|
| Copernicus 30m Global DEM | 21 s | 76 s |
| SRTM 3Sec | 4 s | 4 s |
Solution :
Defect 1 — the default full-tile cache cap is smaller than a tile, always.
Copernicus30mFile.createDirectTile builds the tile on a hardcoded 3600 × 3600 target
grid, so shouldCacheFullTile() always evaluates 3600 × 3600 × 4 = 51 840 000 bytes
(49.4 MiB) against a default snap.dem.copernicus.fullTileCacheMaxBytes of 16 777 216
bytes (16 MiB). The condition is therefore never true for the Copernicus 30 m DEM, at
any latitude: the reader always falls back to 64-row blocks re-read from the GeoTIFF,
and the same value also caps that block cache. The 90 m DEM is unaffected, its tiles
are 1200 × 1200, i.e. 5.8 MB.
Setting -Dsnap.dem.copernicus.fullTileCacheMaxBytes=2000000000 takes the
AddElevation test from 76 s to 6 s, faster than SNAP 13.
Defect 2 — getSample is synchronized. Back-Geocoding samples the DEM sixteen
times per pixel (cubic convolution) from several tile-scheduler threads. They all
serialise on the per-tile monitor, even once the full tile is in memory and nothing
remains but an array lookup.
| Back-Geocoding, same inputs | Defect 1 | Defect 2 | duration |
|---|---|---|---|
| SNAP 13 | — (different reader) | — | 156 s |
| SNAP 14, as shipped | present | present | 371 s |
| SNAP 14, defect 1 fixed | fixed | present | 303 s |
| SNAP 14, defects 1 & 2 fixed | fixed | fixed | 88 s |
The last two rows differ only by the lock, so it costs a factor of 3.4 on its own.
The fix is to stop taking the lock when there is nothing to protect. Once the tile is
fully loaded, getSample only reads an array — any number of threads can do that at
once.
Proposed fix in CopernicusDirectElevationTile.
Defect 1 → the default cap, so that a 3600 × 3600 tile fits:
- getLong("snap.dem.copernicus.fullTileCacheMaxBytes", 16777216L) // 16 MiB
+ getLong("snap.dem.copernicus.fullTileCacheMaxBytes", 67108864L) // 64 MiB
Defect 2 → the read path, after the change. The body of the original getSample
moves verbatim into getSampleSynchronized; loading and the block cache keep the
monitor:
private volatile float[] fullTile; // was: private float[] fullTile;
public float getSample(final int pixelX, final int pixelY) throws Exception {
final float[] cached = fullTile;
if (cached != null) {
return cached[pixelY * targetWidth + pixelX];
}
return getSampleSynchronized(pixelX, pixelY);
}
private synchronized float getSampleSynchronized(final int pixelX, final int pixelY) throws Exception {
// the current body of getSample, unchanged
}
Conclusion : and sorry for the wall of text ![]()
With both fixes, the full chain above runs in 13 min, against 41 min with SNAP 14 as
shipped and 16 min with SNAP 13, and the outputs are bit-identical to stock SNAP 14:
coherence, wrapped phase and displacement, 100 % identical pixels.
| Full chain, same pair | duration |
|---|---|
| SNAP 13 | 16 min |
| SNAP 14, as shipped | 41 min |
| SNAP 14, both defects fixed | 13 min |
Happy to open a pull request on senbox-org/snap-engine if that is the preferred route.