Document multi-GPU support - #19
Conversation
- walkthrough/parallelization: expand the "Multiple GPUs" section with the new `dask.multi-gpu` config flag (automatic dask-cuda cluster over all GPUs), the bring-your-own-cluster path, the single-threaded-client requirement, and when multi-GPU actually helps; add a label for cross-referencing. - reference/default_config.yaml: add the `dask.multi-gpu` option (was out of sync with abtem.yaml). - getting_started/install: note the optional `dask-cuda` dependency and link to the walkthrough. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a tip to the "Multiple GPUs" section noting that more GPUs provide more aggregate device memory, so memory-bound simulations can scale super-linearly. Quote a large benchmark: ~2.0x on two GPUs and ~4.3x on four (beyond the 4x linear ideal), with the caveat that the exact speed-up depends on the calculation parameters and available hardware. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The reference page states that it shows "the full default configuration file", but the copy had drifted: cupy.fft-cache-size was documented as "0 MB" when the shipped default was -1 (and is now "auto"), and the potential.slice-chunk-size, dask.multi-gpu-rmm-pool and dask.multi-gpu-devices keys were missing entirely. The file is now a verbatim copy of abtem/core/abtem.yaml, so it also picks up the per-key comments explaining the fft-cache-size settings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tion The "Using GPUs" section claimed that abTEM sets the CuPy FFT plan cache size to zero because the plans are not worth their memory. That was never the shipped default -- it was -1 (unlimited, CuPy's own default) and is now "auto", 25 % of the device's total memory resolved per device. The recommendation was inverted too: a flat bound measurably costs performance on fast-radix grids, which is why the default is device-relative. The paragraph now describes what the cache does, all four settings, and the uncached-oversized-plan fallback. The "Multiple GPUs" section gains what changed with the multi-GPU hardening work: - to_zarr() distributes like compute(); previously it silently ran the whole scan serially on one device, which is the failure that motivated the work. - The multiprocessing entry-point guard that dask-cuda requires in a script rather than a notebook. - dask.multi-gpu-rmm-pool and dask.multi-gpu-devices, which cover the two cases the section previously sent readers to a hand-built cluster for. - The client configuration reaching the workers, so that precision and device mean the same in a distributed run as in a local one. - A note that a declined multi-GPU request is now reported rather than silently ignored. The scaling tip keeps its argument -- aggregate device memory permits larger batches -- but replaces the earlier super-linear figures with the measured single-node numbers at equal precision: 51 minutes on one 40 GB A100 against 13 minutes on four, 3.86x. The earlier numbers predate the discovery that the client configuration never reached the workers, so a float64 client dispatched float32 work to them; better-than-linear speed-ups were that bug's signature and should not be published. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Running out of memory?" listed only reduce-early, smaller batches and fewer workers, all of which are host-memory answers. Device memory is usually the tighter constraint, so the section now also covers the potential slice-chunk size, the cuFFT plan-cache bound, and the aggregate device memory that more GPUs provide. The "good numbers of gpts" discussion gains a note that the point is sharper on GPU: cuFFT has no kernel for a length with a prime factor above 7 and falls back to Bluestein, which needs a workspace several times the transform size, so an unlucky grid costs memory as well as time. abTEM now warns when it meets such a grid and names the next good size; the note quotes that warning and shows the helpers behind it. The float-precision section notes that the setting is resolved inside each task and now reaches distributed workers -- and that before 1.1 it did not, so double-precision results from a distributed run on an earlier version were actually computed in float32. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Installing dask-cuda pulls in the dask and distributed pins of its own RAPIDS release, which frequently conflict with the versions abTEM was installed with; --no-deps is the practical route, and the install page now says so. The page also gains a cross-reference target so the walkthrough can link to it. The changelog gains the multi-GPU feature entries and, separately, the behaviour changes: the client configuration now reaching distributed workers (which changes distributed results for anyone who relied on a non-default configuration), the bounded cuFFT plan cache, and the halved scan batches on Bluestein grids. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Hi @TomaSusi, (This comment — like the commits it describes — was written by Claude; Paul reviewed and posted it.) Updated for abTEM/abTEM#346, as discussed on that PR. The branch was 18 commits behind Two things need your judgement rather than just review. The super-linear scaling tip is removed. It quoted "~2.0× on two GPUs and about 4.3× on four — beyond the 4× that ideal linear scaling would predict", from a benchmark run in July. That predates #346's finding that abTEM's configuration never reached the dask-cuda workers: a client configured for float64 dispatched float32 work to them, while its single-GPU baseline ran in float64. Better-than-linear speed-ups are precisely that bug's signature — it is what made us re-examine the "12.9× faster than serial" figure in #346, which collapsed to a clean 3.86× once the configuration reached the workers. The tip keeps its argument, which is sound and is why the production case runs at all — more aggregate device memory permits larger batches and fewer rebuilds — but the numbers are now the measured single-node, equal-precision ones: 51 minutes on one 40 GB A100 against 13 minutes on four, 3.86×. If you would like a genuinely super-linear example in the docs, it is worth re-measuring one on the fixed code; I did not want to publish an unverified one next to a PR that explains why such figures were suspect. Should this retarget from The rest, by commit:
"Multiple GPUs" gains what changed in #346:
Nothing was re-executed, so the notebook diffs are prose only. One small question: the Bluestein note shows Best, 🤖 Written by Claude Code on Paul's behalf |
|
Thank you!
In the new documentation (which now includes a /dev version that is roughly kept up to date with abTEM/dev: https://abtem.github.io/doc/dev/intro.html) the API reference is generated with autodoc2, which includes all non-private methods. So I think they will automatically be part of the public API. |
|
I can address the merge conflict easily enough, but one minor issue cropped up during review: parallelization.ipynb:2329 — The multi-GPU benchmark tip claims a 3.86× speed-up from 51 minutes (1 GPU) vs. 13 minutes (4 GPU), but 51/13 = 3.92×, not 3.86×. Either the displayed minute values are rounded from more precise underlying numbers that do produce 3.86×, or one of the three figures is wrong — worth double-checking against the raw benchmark data before merge. |
autodoc2 has no napoleon integration (it never fires the autodoc-process-docstring event napoleon hooks into), so abTEM NumPy-style Parameters/Returns/etc docstring sections were passed through as plain text, and MyST collapsed each section into one unbroken paragraph. Adds a builder-inited hook that runs every collected docstring through sphinx.ext.napoleon.NumpyDocstring (converting to RST field lists) and forces those to be parsed as RST rather than in the ambient MyST context, restoring the old sphinx.ext.napoleon per-parameter rendering. Also works around a crash this exposed in autodoc2 DocstringRenderer: it can raise TypeError (unsupported operand for +: NoneType and int) when docutils emits a system_message without a line number while parsing under an explicit parser option, aborting the whole build. Patched to fail soft (render that one docstring empty, with a warning) instead, matching how docutils own default reporter already handles this case.
The dev-docs workflow installed the same PyPI release as the stable build, so the development banner and version footer were misleading - there was no unreleased code being tested against. Install abTEM from its dev branch for this workflow, and update the banner/footer wording to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Function/method/class headings in the generated API reference repeated every argument with its fully-qualified type and default, duplicating the Parameters list rendered just below. Add a custom autodoc2 MyST renderer that collapses argument lists to (...) in headings, and enable python_use_unqualified_type_names to shorten the remaining return-type annotation. Also add intersphinx mappings for numpy and python so well-formed external type references (e.g. numpy.ndarray) link out instead of staying plain text. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review (TomaSusi): the tip gave 51 minutes against 13 minutes and called it 3.86x, but 51/13 = 3.92. The minute figures were rounded from the raw measurement, and rounding both of them down inflated the implied ratio. The underlying numbers are 3088.8 s and 799.4 s, whose ratio is 3.8639 -- so the 3.86x was right and the minutes were the lossy part. Quoting the seconds makes all three figures consistent and lets a reader verify the ratio, which is the point of putting a benchmark in the documentation at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The PR was retargeted from main to v1.1.0. Only default_config.yaml conflicted: v1.1.0's copy is an exact mirror of abTEM dev's abtem.yaml, and ours is the same mirror taken from multi-gpu-hardening, so the resolution is our version -- it is dev's file plus exactly the keys #346 adds, and drops nothing v1.1.0 had. Restructured the changelog entries to fit the rewritten 1.1.0 section. They had been appended to the old "Upcoming: major features" list, which the merge moved wholesale into "Planned for this release (not yet merged)". #346 is indeed unmerged, so that section is the right home -- but as one entry with sub-bullets rather than three loose bullets and a dangling "Behavior changes" heading. The already-merged #269 entry is left alone, including its statement that cupy.fft-cache-size defaults to -1: that is still true of dev, and the #346 entry records the change to auto. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Reviewed — looks good to merge. Confirmed the merge conflict against |
The merge of v1.1.0 (which added the sampling="auto" rounding cells) dropped the closing bracket/brace/cell-type lines between the GPU Bluestein-note cell and the next inserted cell, corrupting the notebook's JSON and failing the check-notebook-widgets CI check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pre-merge review of PR #20 (which now includes the merged multi-gpu-docs PR #19) found performance_tips.ipynb quoting an outdated UserWarning message: "e.g. by setting gpts explicitly instead of the sampling" is what abtem.core.fft._warn_slow_fft_size said before the autogrid work landed. The shipped message (checked against the actual abTEM dev warning, not just the source) also offers grid.round-to-fast-fft as a remedy -- verified verbatim and quoted correctly now. Also collapses three cells my own earlier commit added (re-importing and re-demonstrating is_fast_fft_size/next_fast_fft_size a second time, duplicating the GPU note's own demo of the same functions on the same 486/512/521 example) into one short paragraph pointing at the existing demo instead of repeating it. Changelog fix in the prior commit (multi-GPU hardening had merged, not "planned") was verified against abTEM PR #346's actual merge time. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Documents opt-in multi-GPU execution (abTEM/abTEM#269) together with the GPU memory and FFT-sizing changes that came out of hardening it (abTEM/abTEM#346), and corrects two statements in the published documentation that never matched the shipped code.
Merged current
mainfirst — this branch predated autodoc2 by 18 commits. Nothing is re-executed, so the notebook diffs are prose only.Corrections to published documentation
cupyto zero, as we find that in most cases the increased memory consumption of the plans are not worth the small speedup they provide". The shipped default was-1(CuPy's own unlimited) and is nowauto— 25 % of the device's total memory, resolved per device. The recommendation is inverted too: a flat bound measured about 7 % slower on fast-radix scans, which is why the default is device-relative rather than a fixed cap. Replaced with what the cache does, all four settings, and the oversized-plan fallback.reference/default_config.yamlis now a verbatim copy ofabtem/core/abtem.yaml. It carried the same0 MBerror and was missingdask.multi-gpu-rmm-pool,dask.multi-gpu-devices(both #346) andpotential.slice-chunk-size(Major GPU update: memory-bounded potential chunking, GPU kernel improvements, and opt-in multi-GPU abTEM#269). The page introduces it as "the full default configuration file", so it is now exactly that, comments included.Multi-GPU walkthrough
The "Multiple GPUs" section covers the
dask.multi-gpuflag, the bring-your-own-cluster path, and the single-threaded-client requirement, and now also:to_zarr()distributes likecompute(). Previously it silently ignored the flag and ran the whole scan on one device — the failure that motivated Multi gpu hardening abTEM#346.if __name__ == "__main__"guard that dask-cuda requires in a script rather than a notebook.dask.multi-gpu-rmm-poolanddask.multi-gpu-devicesas configuration, covering the two cases the section previously sent readers to a hand-built cluster for.precisionanddevicemean the same in a distributed run as in a local one.The scaling tip no longer claims super-linear scaling. Its argument is unchanged — more aggregate device memory permits larger batches and fewer rebuilds — but the earlier "~2.0× on two GPUs, ~4.3× on four" figures predate abTEM/abTEM#346's discovery that the client configuration never reached the workers, so a float64 client dispatched float32 work to them while its single-GPU baseline ran in float64. Better-than-linear speed-ups were that bug's signature. The tip now quotes measured single-node, equal-precision numbers instead: 51 minutes on one 40 GB A100 against 13 minutes on four, 3.86×.
Performance tips appendix
gpts" gains a GPU note: cuFFT's Bluestein fallback costs a workspace several times the transform size, not just time, so an unlucky grid can turn a calculation that fits on a card into one that does not. abTEM now warns and names the next good size; the note quotes that warning and showsis_fast_fft_size/next_fast_fft_size.distributedcluster on an earlier version were actually computed in float32.Install and changelog
getting_started/installnotes the optionaldask-cudadependency, and that it pinsdask/distributedto its own RAPIDS release —--no-depsis usually the practical route. Adds a(getting_started:install)=target so the walkthrough can link to it.abtem/changeloghad no multi-GPU entry. It now has the feature entries and, kept separate, the behaviour changes: the configuration reaching distributed workers (which changes distributed results for anyone relying on a non-default configuration), the bounded plan cache, and the halved scan batches on Bluestein grids.Checks
scripts/check_notebook_widgets.pypasses, both notebooks validate against nbformat, and every MyST cross-reference target in the repo resolves.🤖 Generated with Claude Code