Skip to content

Switch torch dependency from ~=2.9.1 to ~=2.10.0 (silent bfloat16 memory regression)#118

Merged
hanaol merged 6 commits into
mainfrom
hanaol/torch-version-upgrade
May 20, 2026
Merged

Switch torch dependency from ~=2.9.1 to ~=2.10.0 (silent bfloat16 memory regression)#118
hanaol merged 6 commits into
mainfrom
hanaol/torch-version-upgrade

Conversation

@hanaol

@hanaol hanaol commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR updates the torch dependency from ~=2.9.1 to ~=2.10.0 to fix a silent bfloat16 memory regression introduced in torch 2.9.0.

The problem

torch 2.9.0 and 2.9.1 contain a cuDNN regression that inflates the nn.Conv3d bfloat16 forward-pass workspace by 26x -- from ~77 MB to ~2,053 MB -- relative to both the preceding (2.8.0) and following (2.10.0) releases. These numbers were measured on a fixed tensor of shape [1, 32, 64, 64, 64] with a Conv3d(in=32, out=32, k=5, padding=2) layer. float32 memory is completely unaffected (stable at ~123 MB across all versions), confirming the bug is specific to the bfloat16 cuDNN kernel selection path.

This matters because we use (or plan to use) bf16-mixed precision training. This regression would silently consume an extra ~2 GB per Conv3d layer, directly undermining the memory savings that bf16 is supposed to provide -- without any crash or warning.

This issue has been raised in the PyTorch community:

Benchmark results (A100-SXM4-80GB, CUDA 12.8, input shape [1, 32, 64, 64, 64])

Peak GPU memory — float32

torch cuDNN Fwd peak (MB) Bwd peak (MB)
2.8.0 9.1.0.2 123 212
2.9.0 9.1.0.2 123 212
2.9.1 9.1.0.2 123 212
2.10.0 9.1.0.2 123 212
2.11.0 9.1.9.0 123 209

Peak GPU memory — bfloat16

torch cuDNN Fwd peak (MB) Bwd peak (MB)
2.8.0 9.1.0.2 77 111
2.9.0 9.1.0.2 2053 2081
2.9.1 9.1.0.2 2053 2081
2.10.0 9.1.0.2 77 111
2.11.0 9.1.9.0 77 111

The benchmark script is included at scripts/benchmark_conv3d_memory.py and can be run standalone on any CUDA node.

Decision: 2.10.0 vs 2.11.0

Both 2.10.0 and 2.11.0 are clean. This PR pins to 2.10.0 for now. Upgrading to 2.11.0 is possible but introduces a CUDA 13.0 dependency (vs 12.8 for all prior versions), which pulls in a new set of nvidia-*-cu13 libraries and we have not tested it against our full stack (lightning, etc.). Once our ecosystem catches up to CUDA 13.0, bumping to ~=2.11.0 is an option worth revisiting.

Files changed

  • pyproject.toml -- torch~=2.9.1 to torch~=2.10.0
  • uv.lock -- regenerated
  • scripts/benchmark_conv3d_memory.py -- standalone benchmark used to produce the results above

@hanaol

hanaol commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator Author

@claude review this PR 👀 👀 👀

@claude

claude Bot commented Apr 9, 2026

Copy link
Copy Markdown

Claude finished @hanaol's task in 2m 33s —— View job


PR Review: Switch torch ~=2.9.1 → ~=2.10.0

  • Gather context and read changed files
  • Review pyproject.toml change
  • Review benchmark script
  • Review uv.lock changes
  • Post final review

Summary: The fix is correct and well-motivated. The bfloat16 Conv3d regression in torch 2.9.x is real (pytorch/pytorch#166643), and 2.10.0 cleanly resolves it. The benchmark methodology is solid. A few things worth flagging below.


pyproject.toml

The one-line change is correct:

-"torch~=2.9.1",
+"torch~=2.10.0",

~=2.10.0 (compatible release) pins to >=2.10.0, <2.11.0, which is exactly the right constraint — narrow enough to avoid 2.11's CUDA 13.0 dependency, wide enough to pick up any 2.10.x patch. No issues here.


benchmark script (scripts/benchmark_conv3d_memory.py)

Overall well-written. A few observations:

Good:

  • Clear module docstring and inline comments
  • Warmup iterations before measurement — correct practice
  • Averages over BENCH_ITERS=5 for stable timing
  • cuDNN version decoding handles both the pre-9 and post-9 integer formats correctly
  • Uses Path.open() and proper JSON output
  • Structured results with full GPU metadata (SM count, VRAM)

Minor issues:

  1. --output is required (line 106) — the script exits with code 1 when CUDA is unavailable (correct), but fails with an argparse error if --output is omitted. Consider a sensible default like results.json so it can be run with just python benchmark_conv3d_memory.py. Low severity.

  2. Backward memory semantics (lines 79–88): reset_peak_memory_stats() resets the peak counter to zero (not to the current allocation level). This means bwd_peak_mb captures the total peak memory during the backward pass — including the activations already allocated by the fresh out = conv(x) forward. So it's not purely the backward-specific allocation. The table in the PR description labels the columns as "bf16 bwd (MB)" which is slightly ambiguous, but since the methodology is consistent across all torch versions, the relative comparison is valid. Worth adding a comment in the script clarifying this.

  3. out from forward loop leaks into backward loop (line 81): The last out tensor from the forward benchmark loop is still live when the backward loop starts — but it gets immediately overwritten by out = conv(x), so there's no actual memory leak. Just a readability nit.


uv.lock

The changes look exactly right: torch 2.9.1 wheel hashes replaced with torch 2.10.0 equivalents across all platform/Python combinations. The build suffix changed from plain torch-2.9.1-... to torch-2.10.0-2-... and torch-2.10.0-3-... (PyPI build numbers), which is expected.


Commit history

The branch has three commits but two of them are noisy:

  • 45f70f6 — "modules to check later": adds cudnn.benchmark = False + cudnn.deterministic = True to main.py, plus the actual pyproject.toml/uv.lock change
  • b11cc87 — "modified main.py": immediately reverts those two main.py lines

The net effect on main.py is zero, but the history carries exploratory cruft with unhelpful messages. This is fine for a feature branch, but squashing to a single clean commit before merge would make the history easier to read. Not a blocker.


Verdict

Approve with minor suggestions. The core change is correct, minimal, and backed by reproducible evidence. The benchmark script is a valuable addition. The commit history and --output default are polish items, not blockers.

forklady42 and others added 2 commits April 17, 2026 11:33
…semantics, drop stale ref

- Make `--output` default to `results.json` so the script runs standalone without args.
- Add comment explaining that `bwd_peak_mb` reflects peak *during* backward (includes fresh-forward activations held by the autograd graph); relative comparisons across torch versions remain valid.
- `del out` after the forward loop so the stale reference doesn't linger into the backward loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@forklady42 forklady42 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I addressed the minor comments about the benchmark script and reviewed myself

Claude's point about commit history is reasonable, but I'm not concerned as they'll be squashed when merging to main. Could be helpful to keep in mind in the future.

Good to merge ✅

## Summary

This PR adds a benchmark script that trains one sample (task ID) at a
time for 3 epochs under both f32 and bf16-mixed precision on a single
A100 GPU, recording peak GPU memory, forward/backward times, and OOM
status for 10 large-grid samples.

## The problem

The 10 task IDs selected for this benchmark are Materials Project
entries with relatively large charge-density grids, spanning 3.4 M –
46.7 M voxels across a variety of shapes and aspect ratios. It was not
previously known which samples would fit on an A100 (79.3 GB) under f32
vs bf16-mixed, or how training time scales with grid volume. This script
establishes those baselines.

## Benchmark results (A100-SXM4-80GB, CUDA 12.8)

**Config**: `n_channels=32`, `n_residual_blocks=1`, `kernel_size=5`,
`depth=2`, `batch_size=1`, single GPU, 3 epochs per experiment.

| Task ID | Grid shape | Voxels | f32 status | f32 peak (GB) | f32 epoch
(s) | bf16 status | bf16 peak (GB) | bf16 epoch (s) | Mem ratio |

|---------|-----------|:------:|:----------:|:-------------:|:-------------:|:-----------:|:--------------:|:--------------:|:---------:|
| mp-1890579 | 56 × 56 × 1080 | 3.4 M | ✅ | 10.5 | 1.65 | ✅ | 5.5 | 1.07
| 1.91× |
| mp-1849767 | 60 × 60 × 1120 | 4.0 M | ✅ | 12.4 | 1.95 | ✅ | 6.5 | 1.27
| 1.91× |
| mp-1851604 | 60 × 60 × 1120 | 4.0 M | ✅ | 12.4 | 1.93 | ✅ | 6.5 | 1.25
| 1.91× |
| mp-1862536 | 80 × 80 × 1024 | 6.6 M | ✅ | 19.9 | 3.17 | ✅ | 10.4 |
1.94 | 1.91× |
| mp-1847208 | 1120 × 84 × 84 | 7.9 M | ✅ | 23.9 | 3.87 | ✅ | 12.5 |
2.46 | 1.91× |
| mp-1936557 | 80 × 756 × 216 | 13.1 M | ✅ | 39.1 | 6.47 | ✅ | 20.4 |
3.73 | 1.91× |
| mp-1850168 | 972 × 240 × 128 | 29.9 M | ❌ OOM | 70.2 | — | ✅ | 46.3 |
8.77 | — |
| mp-1887804 | 320 × 320 × 320 | 32.8 M | ❌ OOM | 68.8 | — | ✅ | 50.6 |
169.91 | — |
| mp-1889246 | 540 × 144 × 432 | 33.6 M | ❌ OOM | 70.6 | — | ✅ | 51.9 |
208.94 | — |
| mp-1871122 | 360 × 360 × 360 | 46.7 M | ❌ OOM | 71.4 | — | ❌ OOM |
71.9 | — | — |

### Summary

| Precision | ✅ Completed | ❌ OOM |
|-----------|:-----------:|:------:|
| f32 | 6 / 10 | 4 / 10 |
| bf16-mixed | 9 / 10 | 1 / 10 (mp-1871122, 360³) |

### Key findings

- **Memory**: bf16-mixed yields a consistent **1.91× reduction** in peak
GPU memory for all grids that fit under both precisions.
- **Speed**: bf16-mixed is **1.5–1.7× faster** per epoch on grids ≤ 13 M
voxels; the backward pass benefits the most (~2.2×).
- **OOM threshold on A100**: ~13 M voxels under f32; ~33 M voxels under
bf16-mixed.
- **Largest grid (360³, 46.7 M voxels)**: exceeds A100 capacity even
with bf16-mixed.
- **Very large grids (320³, 540×144×432)**: fit under bf16-mixed but
epoch times are 170–209 s, dominated by the backward pass (~144–176 s
through large skip connections).

## Files

- `scripts/benchmark_precision.py` -- Runs 3-epoch single-GPU training
for each (task_id, precision) pair, recording CUDA-event
forward/backward times and peak GPU memory

---------

Co-authored-by: Hananeh Oliaei <ho0950@della-vis2.princeton.edu>
Co-authored-by: Betsy Cannon <betsy@openathena.ai>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hanaol
hanaol merged commit a2be897 into main May 20, 2026
3 checks passed
@hanaol
hanaol deleted the hanaol/torch-version-upgrade branch May 20, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants