Weijie Lyu Claude Opus 4.8 (1M context) commited on
Commit
d0d5411
·
1 Parent(s): 4ff86b7

Fix real OOM: 4-D SDPA layout; cu130 is ZeroGPU-native, drop cu128 cruft

Browse files

GPU-DIAG showed torch 2.11.0+cu130 persists and diff-gaussian compiles
against the system CUDA 13.0 toolkit -- i.e. ZeroGPU natively ships
cu130 + a matching CUDA 13.0 toolkit. So cu130 was never the problem
and the cu128/nvcc machinery was unnecessary: remove the pip-nvcc
CUDA_HOME logic and the nvidia-cuda-nvcc/cccl requirements; compile
against the matching system toolkit.

The actual failure (42 GiB free yet OOM at first attention) was the
SDPA call receiving 3-D tensors, which forces SDPA's math backend to
materialize the full multi-view attention matrix (tens of GB). Reshape
q/k/v to 4-D (batch, heads, seq, head_dim) so SDPA uses its flash /
memory-efficient backend -- the efficient path xformers used to provide.

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

app.py CHANGED
@@ -99,41 +99,10 @@ except ImportError:
99
  print("Installing diff-gaussian-rasterization (compiling for detected CUDA arch)...")
100
  env = os.environ.copy()
101
 
102
- # Compile against the pip-installed CUDA 12.8 toolkit (nvidia-cuda-nvcc-cu12),
103
- # NOT the container's CUDA 13.0 toolkit. torch here is the cu128 build ZeroGPU
104
- # requires, and torch's build check errors on a major CUDA mismatch (nvcc 13 vs
105
- # torch 12.8). Point CUDA_HOME at the pip nvcc and feed the runtime/cub headers
106
- # and cudart lib via CPATH/LIBRARY_PATH so nvcc 12.8 matches torch 12.8.
107
- import site, glob
108
- _search_dirs = list(site.getsitepackages())
109
- try:
110
- _search_dirs.append(site.getusersitepackages())
111
- except Exception:
112
- pass
113
- _nvcc_bins = []
114
- for _d in _search_dirs:
115
- _nvcc_bins += glob.glob(os.path.join(_d, "nvidia", "cuda_nvcc*", "bin", "nvcc"))
116
- if _nvcc_bins:
117
- _nvcc_home = os.path.dirname(os.path.dirname(_nvcc_bins[0])) # .../nvidia/cuda_nvcc
118
- _nv = os.path.dirname(_nvcc_home) # .../nvidia
119
- env["CUDA_HOME"] = _nvcc_home
120
- env["PATH"] = os.path.join(_nvcc_home, "bin") + os.pathsep + env.get("PATH", "")
121
- _incs = [p for p in (os.path.join(_nv, "cuda_runtime", "include"),
122
- os.path.join(_nv, "cuda_cccl", "include")) if os.path.isdir(p)]
123
- if _incs:
124
- env["CPATH"] = os.pathsep.join(_incs + ([env["CPATH"]] if env.get("CPATH") else []))
125
- _rt_lib = os.path.join(_nv, "cuda_runtime", "lib")
126
- if os.path.isdir(_rt_lib):
127
- env["LIBRARY_PATH"] = _rt_lib + os.pathsep + env.get("LIBRARY_PATH", "")
128
- env["LD_LIBRARY_PATH"] = _rt_lib + os.pathsep + env.get("LD_LIBRARY_PATH", "")
129
- print(f"[BUILD] using pip CUDA toolkit: CUDA_HOME={_nvcc_home}")
130
- else:
131
- print("⚠️ pip CUDA nvcc (nvidia-cuda-nvcc-cu12) not found under site-packages/nvidia; "
132
- "falling back to system nvcc (will mismatch cu128 torch). "
133
- "If this persists, Factory Reboot the Space to clear the stale build cache.")
134
-
135
- # Build a cross-arch set incl. Blackwell sm_120 (the ZeroGPU GPU is cap 12.0);
136
- # the build stage may not see a GPU, so don't rely on get_device_capability.
137
  env["TORCH_CUDA_ARCH_LIST"] = "8.0;8.6;8.9;9.0;12.0+PTX"
138
 
139
  # --no-build-isolation: the package's setup.py imports torch to detect the
 
99
  print("Installing diff-gaussian-rasterization (compiling for detected CUDA arch)...")
100
  env = os.environ.copy()
101
 
102
+ # ZeroGPU ships a cu130 torch and a matching CUDA 13.0 toolkit, so compile
103
+ # against the system toolkit (no version mismatch). Include Blackwell sm_120
104
+ # (the ZeroGPU GPU is capability 12.0); the build stage may not see a GPU, so
105
+ # use a fixed cross-arch set rather than get_device_capability.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  env["TORCH_CUDA_ARCH_LIST"] = "8.0;8.6;8.9;9.0;12.0+PTX"
107
 
108
  # --no-build-isolation: the package's setup.py imports torch to detect the
mvdiffusion/models/transformer_mv2d_image.py CHANGED
@@ -725,20 +725,25 @@ class MVAttnProcessor:
725
  # value = rearrange(values, 'b t f d c -> (b t) (f d) c')
726
 
727
 
728
- query = attn.head_to_batch_dim(query).contiguous()
729
- key = attn.head_to_batch_dim(key).contiguous()
730
- value = attn.head_to_batch_dim(value).contiguous()
 
 
 
 
 
 
 
 
 
731
 
732
- # Memory-efficient attention via PyTorch SDPA. Mathematically equivalent to
733
- # the get_attention_scores (baddbmm) + softmax + bmm full-attention path, but
734
- # it never materializes the full attention matrix -- that full matrix OOMs on
735
- # multi-view 512-res inputs. xformers' kernels were the original efficient
736
- # path but don't support the Blackwell GPU, so use SDPA (flash/mem-efficient
737
- # backend), which does and runs on Blackwell.
738
  hidden_states = F.scaled_dot_product_attention(
739
  query, key, value, attn_mask=attention_mask, scale=attn.scale
740
  )
741
- hidden_states = attn.batch_to_head_dim(hidden_states)
 
 
742
 
743
  # linear proj
744
  hidden_states = attn.to_out[0](hidden_states)
 
725
  # value = rearrange(values, 'b t f d c -> (b t) (f d) c')
726
 
727
 
728
+ # Memory-efficient attention via PyTorch SDPA. Reshape to 4-D
729
+ # (batch, heads, seq, head_dim): SDPA only dispatches to its flash /
730
+ # memory-efficient backends for 4-D inputs -- with the 3-D head_to_batch_dim
731
+ # layout it falls back to the math backend, which materializes the full
732
+ # multi-view attention matrix (tens of GB at 512-res) and OOMs. xformers was
733
+ # the original efficient path but doesn't support the Blackwell GPU.
734
+ # reshape (not view): the multi-view key/value are non-contiguous.
735
+ bsz = query.shape[0]
736
+ head_dim = query.shape[-1] // attn.heads
737
+ query = query.reshape(bsz, -1, attn.heads, head_dim).transpose(1, 2)
738
+ key = key.reshape(bsz, -1, attn.heads, head_dim).transpose(1, 2)
739
+ value = value.reshape(bsz, -1, attn.heads, head_dim).transpose(1, 2)
740
 
 
 
 
 
 
 
741
  hidden_states = F.scaled_dot_product_attention(
742
  query, key, value, attn_mask=attention_mask, scale=attn.scale
743
  )
744
+ hidden_states = hidden_states.transpose(1, 2).reshape(
745
+ bsz, -1, attn.heads * head_dim
746
+ ).to(query.dtype)
747
 
748
  # linear proj
749
  hidden_states = attn.to_out[0](hidden_states)
requirements.txt CHANGED
@@ -1,13 +1,9 @@
1
  # torch 2.11.0 is ZeroGPU-supported and the version line where torchvision 0.26.0
2
  # and xformers 0.0.35 agree (xformers 0.0.33 hard-pinned torch==2.9.0, which ZeroGPU
3
- # rejects; 0.0.35 relaxed to >=2.10). This is the cu128 (CUDA 12.8) build ZeroGPU
4
- # ships -- required for ZeroGPU's GPU memory management to work.
5
  torch==2.11.0
6
  torchvision==0.26.0
7
- # CUDA 12.8 compiler + headers so diff-gaussian-rasterization compiles against a
8
- # toolkit matching cu128 torch (the container only ships CUDA 13.0). See app.py.
9
- nvidia-cuda-nvcc-cu12~=12.8.0
10
- nvidia-cuda-cccl-cu12~=12.8.0
11
  numpy==1.26.4
12
  pillow==10.4.0
13
  diffusers[torch]==0.30.3
 
1
  # torch 2.11.0 is ZeroGPU-supported and the version line where torchvision 0.26.0
2
  # and xformers 0.0.35 agree (xformers 0.0.33 hard-pinned torch==2.9.0, which ZeroGPU
3
+ # rejects; 0.0.35 relaxed to >=2.10). ZeroGPU provides the cu130 build + a matching
4
+ # CUDA 13.0 toolkit, so diff-gaussian-rasterization compiles against the system toolkit.
5
  torch==2.11.0
6
  torchvision==0.26.0
 
 
 
 
7
  numpy==1.26.4
8
  pillow==10.4.0
9
  diffusers[torch]==0.30.3