Skip to content

Commit 0252d07

Browse files
lguerardclaude
andcommitted
feat: ✨ live Dask dashboard for single-GPU runs
On a GPU run with no existing client, start a 1-worker/1-thread in-process LocalCluster: GPU evals stay serial (no VRAM contention) but a Dask dashboard is exposed and its URL logged, so progress is visible. Falls back to the threaded scheduler if distributed/bokeh are absent; a user-provided cluster is used as-is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 731e4c2 commit 0252d07

2 files changed

Lines changed: 44 additions & 1 deletion

File tree

docs/guide/performance.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@ merge step are sized to the host automatically:
1717
The RAM figure is read live via `psutil`; without it, a conservative default is
1818
used instead of guessing high.
1919

20+
## Live progress dashboard (GPU runs)
21+
22+
A single-GPU run still gets a **Dask dashboard**: patchworks spins up a tiny
23+
1-worker / 1-thread in-process cluster, which keeps GPU evaluations serial (no
24+
VRAM contention) while exposing the dashboard so you can watch tiles stream
25+
through. The URL is logged at the start of staging:
26+
27+
```text
28+
INFO:patchworks._core:Dask dashboard for this run: http://127.0.0.1:8787/status
29+
```
30+
31+
This needs `distributed` (and `bokeh` for the UI) installed; if they are
32+
missing, patchworks logs a warning and falls back to the threaded scheduler
33+
(no dashboard, same result). A cluster you start yourself
34+
(`make_local_cluster`) is used as-is instead.
35+
2036
## Overriding the worker count
2137

2238
```python

src/patchworks/_core.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,31 @@ def active_fn(block, block_info=None):
332332
import dask as _dask
333333

334334
_tile_nbytes = int(np.prod(labeled.chunksize)) * labeled.dtype.itemsize
335-
if _active is None:
335+
_temp_cluster = None
336+
_temp_client = None
337+
if _active is None and use_gpu:
338+
# Single-GPU runs still get a live Dask dashboard: a 1-worker /
339+
# 1-thread in-process cluster keeps GPU evals serial (no VRAM
340+
# contention) while exposing the dashboard for progress.
341+
try:
342+
from dask.distributed import Client, LocalCluster
343+
344+
_temp_cluster = LocalCluster(
345+
n_workers=1, threads_per_worker=1, processes=False
346+
)
347+
_temp_client = Client(_temp_cluster)
348+
logger.info(
349+
"Dask dashboard for this run: %s",
350+
_temp_client.dashboard_link,
351+
)
352+
except Exception as exc: # no distributed/bokeh → threaded fallback
353+
logger.warning(
354+
"Could not start a dashboard cluster (%s); "
355+
"falling back to the threaded scheduler.",
356+
exc,
357+
)
358+
359+
if _distributed_client() is None:
336360
_workers = (
337361
max_workers
338362
if max_workers is not None
@@ -363,6 +387,9 @@ def active_fn(block, block_info=None):
363387
logger.info("Staging tiles to %s …", stage_path)
364388
with _sched_ctx:
365389
_stage_to_zarr(labeled, stage_path, "staged", progress)
390+
if _temp_client is not None:
391+
_temp_client.close()
392+
_temp_cluster.close()
366393
labeled = da.from_zarr(stage_path, component="staged")
367394

368395
# NB: no post-staging skip-count pass here — counting skipped tiles by

0 commit comments

Comments
 (0)