Skip to content

perf(geotiff): _write_vrt_tiled uses threaded dask scheduler (#1714)#1725

Merged
brendancol merged 2 commits into
mainfrom
deep-sweep-performance-geotiff-2026-05-12-aa0e4ef9-02
May 12, 2026
Merged

perf(geotiff): _write_vrt_tiled uses threaded dask scheduler (#1714)#1725
brendancol merged 2 commits into
mainfrom
deep-sweep-performance-geotiff-2026-05-12-aa0e4ef9-02

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Summary

_write_vrt_tiled builds one dask.delayed task per output tile then
runs them all through dask.compute. Each task writes to its own file
path and never touches shared mutable Python state, so the writes are
embarrassingly parallel. The prior code passed
scheduler='synchronous', which serialised every tile on the calling
thread.

Switch to scheduler='threads'. zlib / zstd / LZW release the GIL
during compression, so threading delivers real wall-time wins on the
compression stage.

Microbench on a 16-thread box, 4096x4096 float32 dask DataArray with
chunks=256 (256 output tiles), zstd compression:

  • before: 0.49 s
  • after: 0.33 s (~33% reduction)

The gain grows with tile count and codec cost.

Closes #1714.

Test plan

  • test_vrt_tiled_uses_threaded_scheduler: patches dask.compute and asserts the writer passes scheduler='threads'.
  • test_vrt_tiled_threaded_write_produces_all_tiles: 4x4 chunked input produces exactly 16 tile files.
  • test_vrt_tiled_threaded_write_is_deterministic: run the same write twice into separate dirs, byte-compare every tile, catch any concurrent-write race.
  • Existing xrspatial/geotiff/tests/test_vrt_tiled_metadata_1606.py, test_polish_1488.py, test_vrt_write.py, test_streaming_write_parallel.py, and test_writer.py (67 tests) all pass.

@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 12, 2026
@brendancol brendancol requested a review from Copilot May 12, 2026 19:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improve performance of VRT tiled GeoTIFF writes by running per-tile dask.delayed write tasks using Dask’s threaded scheduler, and add tests that pin the scheduler choice and validate correctness/determinism of concurrent tile writes.

Changes:

  • Switch _write_vrt_tiled from scheduler='synchronous' to scheduler='threads' when calling dask.compute.
  • Add a new test module validating scheduler selection, tile count output, and deterministic (byte-identical) output across repeated runs.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
xrspatial/geotiff/__init__.py Uses Dask’s threaded scheduler for VRT tile write task execution and documents rationale.
xrspatial/geotiff/tests/test_vrt_tiled_scheduler_1714.py Adds coverage for threaded scheduling and concurrent tile-write correctness/determinism.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import dask
import dask.array as da
import numpy as np
import pytest
Comment on lines +93 to +96
return {
os.path.basename(p): open(p, "rb").read()
for p in sorted(glob.glob(os.path.join(tiles_dir, "*.tif")))
}
Each delayed task in _write_vrt_tiled writes one tile to its own
output path with no shared mutable Python state, so the writes are
embarrassingly parallel. The prior code called dask.compute with
scheduler='synchronous', which forced every tile through the calling
thread one at a time.

Switch to scheduler='threads'. zlib/zstd/LZW release the GIL during
compression, so threading delivers real wall-time wins on the
compression stage. Microbench: 4096x4096 float32 dask DataArray
with chunks=256 (256 output tiles) at zstd compression drops from
0.49s to 0.33s (~33% reduction).

Adds tests covering the scheduler choice, the tile-file inventory,
and a determinism check that runs the same write twice and compares
every tile byte-for-byte to catch any race regression.
@brendancol brendancol force-pushed the deep-sweep-performance-geotiff-2026-05-12-aa0e4ef9-02 branch from c3fc5e4 to 05fea7a Compare May 12, 2026 19:29
- Remove unused pytest import in test_vrt_tiled_scheduler_1714.py
- Use Path.read_bytes() in tile-byte comparison to avoid leaking file
  descriptors (the previous dict comprehension opened files via
  ``open(p, "rb").read()`` without a context manager)
@brendancol brendancol merged commit 258d16c into main May 12, 2026
4 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(geotiff): _write_vrt_tiled uses synchronous scheduler, defeating parallel tile writes

2 participants