HumboldtJoker commited on
Commit
82e4fd6
Β·
verified Β·
1 Parent(s): bba63c0

Add spec: runpod_oom_debug.md

Browse files
Files changed (1) hide show
  1. specs/runpod_oom_debug.md +349 -0
specs/runpod_oom_debug.md ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # RunPod H200 SXM SIGKILL Debug: Qwen3.6-35B-A3B Full SFT
2
+
3
+ **Date:** 2026-06-30
4
+ **Pod:** vocal_coral_quokka (jifd0w19x1c61j)
5
+ **Status:** Root cause identified. Full SFT is not viable on this pod configuration.
6
+
7
+ ---
8
+
9
+ ## Root Cause: Container Memory Limit, Not Host RAM
10
+
11
+ ### The Lie `free -h` Tells You
12
+
13
+ RunPod containers see **host RAM** (2TB) in `/proc/meminfo` and `free -h`, but the
14
+ actual memory available to the container is enforced by **cgroup limits** that are
15
+ much lower. For a 1x H200 SXM pod, RunPod allocates:
16
+
17
+ | Resource | Spec per 1x H200 SXM |
18
+ |-----------------|----------------------|
19
+ | VRAM | 141 GB HBM3e |
20
+ | **System RAM** | **276 GB** |
21
+ | vCPUs | 24 |
22
+
23
+ **The container is capped at ~276 GB system RAM.** When a process exceeds this,
24
+ the Linux OOM killer sends SIGKILL (-9). There is no warning, no OOM error message,
25
+ just instant death. This matches the observed behavior exactly.
26
+
27
+ ### How to Verify Inside the Pod
28
+
29
+ ```bash
30
+ # cgroup v1 (most RunPod pods)
31
+ cat /sys/fs/cgroup/memory/memory.limit_in_bytes
32
+
33
+ # cgroup v2
34
+ cat /sys/fs/cgroup/memory.max
35
+
36
+ # What the process THINKS is available (WRONG - shows host RAM)
37
+ free -h
38
+
39
+ # What's actually enforced (check OOM events)
40
+ dmesg | grep -i "oom\|killed"
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Memory Math: Why Full SFT Cannot Fit
46
+
47
+ ### Qwen3.6-35B-A3B Architecture
48
+
49
+ - **Total parameters:** 35B (35 billion)
50
+ - **Active per token:** 3B (8 routed + 1 shared expert of 256 total)
51
+ - **Layers:** 40
52
+ - **Experts:** 256 per MoE layer, 9 active
53
+ - **Hidden dim:** 2,048
54
+ - **Architecture:** Sparse MoE β€” inference is cheap, but **training touches ALL 35B params**
55
+
56
+ ### The Critical MoE Training Problem
57
+
58
+ During inference, only 3B parameters are active per token. But during **training**:
59
+ - **Forward pass:** routes through selected experts (cheap)
60
+ - **Backward pass:** computes gradients for ALL activated expert parameters
61
+ - **Optimizer step:** maintains states for **ALL 35B parameters** regardless of activation
62
+
63
+ This means training memory scales with TOTAL params (35B), not active params (3B).
64
+
65
+ ### Memory Breakdown for Full SFT (Single GPU)
66
+
67
+ | Component | Calculation | Size |
68
+ |----------------------------|-----------------------|----------|
69
+ | Model weights (bf16) | 35B x 2 bytes | **70 GB** |
70
+ | fp32 master weights | 35B x 4 bytes | **140 GB** |
71
+ | AdamW momentum (fp32) | 35B x 4 bytes | **140 GB** |
72
+ | AdamW variance (fp32) | 35B x 4 bytes | **140 GB** |
73
+ | Gradients (bf16) | 35B x 2 bytes | **70 GB** |
74
+ | **Total (steady state)** | | **560 GB** |
75
+ | Activation memory | batch/seq dependent | 10-50 GB+ |
76
+ | CUDA/PyTorch overhead | fragmentation, buffers| 5-15 GB |
77
+ | **Peak during init** | temporary copies | **600-700 GB** |
78
+
79
+ ### What Each ZeRO Stage Actually Requires
80
+
81
+ **ZeRO-2, no offload (attempt 1):**
82
+ - GPU needs: 70 (weights) + 140 (master) + 280 (optimizer) + 70 (grads) = **560 GB**
83
+ - Available GPU: 141 GB
84
+ - Result: `torch.OutOfMemoryError` trying to allocate 129 GB. Correct behavior.
85
+
86
+ **ZeRO-2, CPU offload (attempt 2):**
87
+ - GPU: ~70 GB model weights + activations + grad buffers β‰ˆ 100-130 GB
88
+ - CPU: fp32 master weights (140 GB) + optimizer states (280 GB) = **420 GB**
89
+ - Available CPU (container): **276 GB**
90
+ - Deficit: **~144 GB over limit**
91
+ - Result: SIGKILL during init. 100% CPU for 2 min = optimizer state allocation eating RAM until OOM killer fires.
92
+
93
+ **ZeRO-3, CPU offload (attempt 3):**
94
+ - GPU: only active parameter slice + activations β‰ˆ 20-40 GB
95
+ - CPU: partitioned states BUT with 1 GPU, nothing to partition across
96
+ - CPU needs: same ~420-560 GB for optimizer + master weights
97
+ - **Additionally:** ZeRO-3 does NOT support MoE models. DeepSpeed raises `AssertionError: MoE not supported with Stage 3` in current versions.
98
+ - Even if it initialized, peak init memory creates temporary copies that spike higher
99
+ - Result: SIGKILL after ~4 min of init. Same container RAM ceiling.
100
+
101
+ ### The ZeRO-3 + MoE Incompatibility
102
+
103
+ This is a confirmed, documented limitation:
104
+ - **GitHub Issue #2870** (filed Feb 2023, still open): "ZeRO stage 3 support for mixture-of-experts (MoE) layer" β€” explicitly unsupported
105
+ - **GitHub Issue #7156** (open): Even with ZeRO-2, expert optimizer states are NOT partitioned β€” only non-expert params get ZeRO treatment
106
+ - The MoE expert parameters maintain full unpartitioned optimizer states, making them the dominant memory consumer
107
+
108
+ ### The ms-swift Confirmation
109
+
110
+ GitHub Issue **modelscope/ms-swift#6473** documents the identical failure:
111
+ - **Same model family:** Qwen3-30B-A3B (MoE)
112
+ - **Same setup:** DeepSpeed ZeRO-2 with CPU offload
113
+ - **Same result:** System RAM climbed to 1.7 TiB, OOM killed
114
+ - **Hardware:** 8x H200 GPUs, 1.5TB RAM, 96 CPU cores
115
+ - **Key insight:** Even with 8 GPUs and 1.5TB RAM, full SFT of this MoE model exhausted all system memory
116
+
117
+ If 8x H200s with 1.5TB RAM cannot do this, 1x H200 with 276 GB has zero chance.
118
+
119
+ ---
120
+
121
+ ## Viable Alternatives (Ranked by Practicality)
122
+
123
+ ### Option 1: LoRA Fine-Tuning with Unsloth (RECOMMENDED)
124
+
125
+ **Why it works:** LoRA only trains adapter weights (~2-3% of params), so optimizer states
126
+ are proportional to LoRA rank, not total model params.
127
+
128
+ | Metric | Value |
129
+ |----------------------------|------------------------|
130
+ | Trainable params | ~931M of 36B (2.58%) |
131
+ | VRAM required (bf16 LoRA) | ~63-74 GB |
132
+ | System RAM required | ~40-80 GB |
133
+ | Fits on 1x H200 SXM? | **Yes** (141 GB VRAM) |
134
+ | Framework | Unsloth or ms-swift |
135
+
136
+ **Unsloth Configuration:**
137
+ ```python
138
+ from unsloth import FastLanguageModel
139
+
140
+ model, tokenizer = FastLanguageModel.from_pretrained(
141
+ model_name="Qwen/Qwen3.6-35B-A3B",
142
+ max_seq_length=2048, # Safe ceiling for 1 GPU
143
+ dtype=torch.bfloat16,
144
+ load_in_4bit=False, # QLoRA NOT recommended for MoE (BnB limitation)
145
+ )
146
+
147
+ model = FastLanguageModel.get_peft_model(
148
+ model,
149
+ r=16, # LoRA rank
150
+ lora_alpha=16, # alpha == r, not r*2
151
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
152
+ "gate_proj", "up_proj", "down_proj"],
153
+ lora_dropout=0,
154
+ use_gradient_checkpointing="unsloth",
155
+ )
156
+ ```
157
+
158
+ **Critical Gotchas:**
159
+ 1. Set `UNSLOTH_COMPILE_DISABLE=1` before imports (bf16/fp32 dtype mismatch in compiled MoE kernels)
160
+ 2. Set `dataloader_num_workers=0` and `dataset_num_proc=1` (multiprocessing deadlock with large model)
161
+ 3. Do NOT use QLoRA/4-bit β€” BitsandBytes does not support MoE architectures
162
+ 4. Router layer is NOT fine-tuned by default (and shouldn't be for SFT)
163
+ 5. Max safe sequence length on single H200: ~2048 tokens (activation memory explodes at 4096)
164
+ 6. vLLM cannot load LoRA adapters directly β€” merge weights before serving
165
+
166
+ **Unsloth Split LoRA** provides additional MoE-specific optimization: reorders matmul
167
+ to compute `(X @ loraA) @ loraB` instead of materializing `loraA @ loraB`, saving ~35%
168
+ VRAM and providing ~2x speedup.
169
+
170
+ ### Option 2: Multi-GPU with Expert Parallelism (Megatron-SWIFT)
171
+
172
+ **Why it works:** Distributes expert params AND their optimizer states across GPUs.
173
+ Each GPU only holds a subset of experts.
174
+
175
+ | Metric | Value |
176
+ |----------------------------|--------------------------|
177
+ | GPUs required | 4-8x (H100/H200) |
178
+ | Framework | ms-swift + Megatron |
179
+ | Expert parallelism size | 4 or 8 |
180
+ | Per-GPU VRAM | ~40 GB with EP=8 |
181
+ | System RAM per node | 1+ TB recommended |
182
+
183
+ **ms-swift Megatron configuration for Qwen3.5-35B-A3B:**
184
+ ```bash
185
+ NPROC_PER_NODE=8 swift sft \
186
+ --model Qwen/Qwen3.6-35B-A3B \
187
+ --train_type full \
188
+ --expert_model_parallel_size 8 \
189
+ --tuner_type lora --lora_rank 8 --lora_alpha 32 \
190
+ --moe_permute_fusion true \
191
+ --moe_grouped_gemm true \
192
+ --moe_shared_expert_overlap true \
193
+ --moe_aux_loss_coeff 1e-6 \
194
+ --offload_model true \
195
+ --offload_optimizer true
196
+ ```
197
+
198
+ **Cost:** 8x H200 SXM on RunPod = ~$28.72/hr (community) or ~$35.12/hr (secure).
199
+ This is expensive but may be necessary for true full-parameter training.
200
+
201
+ **Warning:** Even 8x H200 with 1.5TB RAM has been reported to OOM on full SFT
202
+ (ms-swift#6473). Expert parallelism with LoRA is the safer configuration.
203
+
204
+ ### Option 3: Selective Layer Unfreezing
205
+
206
+ **Hybrid approach:** Freeze most experts, unfreeze shared expert + attention + embeddings.
207
+
208
+ ```python
209
+ # Freeze all expert parameters
210
+ for name, param in model.named_parameters():
211
+ if "experts" in name and "shared" not in name:
212
+ param.requires_grad = False
213
+
214
+ # Keep trainable: attention, shared expert, embeddings, LM head
215
+ # Trainable params: ~3-5B instead of 35B
216
+ # Optimizer states: ~24-40 GB instead of 420 GB
217
+ ```
218
+
219
+ This fits comfortably in 276 GB system RAM on 1x H200 and gives more capacity
220
+ than LoRA while avoiding the full 35B optimizer state problem.
221
+
222
+ ### Option 4: Beast Server (NOT Recommended)
223
+
224
+ The 3x RTX 3090 server has:
225
+ - 72 GB total VRAM (3x 24 GB)
226
+ - ~186 GB system RAM
227
+ - Worse than 1x H200 in every dimension for this task
228
+
229
+ Even with ZeRO-2 across 3 GPUs, the optimizer states for 35B params would need
230
+ ~420 GB CPU RAM. Does not fit. Would only work for LoRA, and the H200 is better
231
+ for LoRA anyway (more VRAM per device, faster memory bandwidth).
232
+
233
+ ---
234
+
235
+ ## Diagnostic Checklist for the Running Pod
236
+
237
+ If the pod is still accessible, run these to confirm the analysis:
238
+
239
+ ```bash
240
+ # 1. Check ACTUAL container memory limit
241
+ cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null || \
242
+ cat /sys/fs/cgroup/memory.max 2>/dev/null
243
+
244
+ # 2. Compare with what free reports (will show host RAM, not limit)
245
+ free -h
246
+
247
+ # 3. Check OOM kill events
248
+ dmesg | grep -i "oom\|killed\|memory" | tail -20
249
+
250
+ # 4. Check cgroup memory usage at time of kill
251
+ cat /sys/fs/cgroup/memory/memory.usage_in_bytes 2>/dev/null || \
252
+ cat /sys/fs/cgroup/memory.current 2>/dev/null
253
+
254
+ # 5. Check if swap is available (usually no on RunPod)
255
+ swapon --show
256
+
257
+ # 6. Check GPU memory
258
+ nvidia-smi --query-gpu=memory.total,memory.used,memory.free --format=csv
259
+ ```
260
+
261
+ ---
262
+
263
+ ## If You Still Want to Attempt Full SFT
264
+
265
+ The absolute minimum requirements for full-parameter SFT of Qwen3.6-35B-A3B:
266
+
267
+ 1. **Multiple GPUs with Expert Parallelism** β€” not ZeRO-3 (unsupported for MoE)
268
+ 2. **At minimum 8 GPUs** with expert_parallel_size=8
269
+ 3. **System RAM:** 2+ TB to be safe (the ms-swift issue showed 1.5TB was not enough)
270
+ 4. **Framework:** ms-swift with Megatron backend, NOT raw DeepSpeed
271
+ 5. **Use LoRA even in multi-GPU** β€” full param SFT of 35B MoE may be fundamentally
272
+ impractical without custom expert-aware optimizer state partitioning
273
+ 6. **sub_group_size:** Set to 1e8 or lower (default 1e9) to reduce init memory peak
274
+ 7. **pin_memory: false** in offload config to reduce pinned memory allocation
275
+ 8. **Disable overlap_comm** during init if possible
276
+
277
+ ### DeepSpeed Config Tweaks (if retrying ZeRO-2 on multi-GPU)
278
+
279
+ ```json
280
+ {
281
+ "zero_optimization": {
282
+ "stage": 2,
283
+ "offload_optimizer": {
284
+ "device": "cpu",
285
+ "pin_memory": false,
286
+ "buffer_count": 4,
287
+ "fast_init": false
288
+ },
289
+ "offload_param": {
290
+ "device": "cpu",
291
+ "pin_memory": false
292
+ },
293
+ "allgather_partitions": true,
294
+ "allgather_bucket_size": 5e7,
295
+ "overlap_comm": false,
296
+ "reduce_scatter": true,
297
+ "reduce_bucket_size": 5e7,
298
+ "contiguous_gradients": true,
299
+ "sub_group_size": 1e8
300
+ },
301
+ "bf16": { "enabled": true },
302
+ "train_micro_batch_size_per_gpu": 1,
303
+ "gradient_accumulation_steps": 4,
304
+ "gradient_clipping": 1.0
305
+ }
306
+ ```
307
+
308
+ Key changes from default:
309
+ - `pin_memory: false` β€” pinned memory doubles the RAM footprint by keeping non-swappable copies
310
+ - `sub_group_size: 1e8` β€” processes optimizer updates in smaller tiles, reducing peak init memory
311
+ - `overlap_comm: false` β€” prevents double-buffering during init phase
312
+ - `fast_init: false` β€” sequential initialization instead of bulk allocation
313
+ - Reduced bucket sizes (5e7 vs default 5e8) β€” smaller communication buffers
314
+
315
+ ---
316
+
317
+ ## Decision Matrix
318
+
319
+ | Approach | Fits 1x H200? | Quality vs Full SFT | Cost/hr | Complexity |
320
+ |-------------------------|----------------|----------------------|----------|------------|
321
+ | LoRA (Unsloth) | YES | ~95% for most tasks | $3.59 | Low |
322
+ | Selective unfreeze | YES | ~97% | $3.59 | Medium |
323
+ | 8x H200 + EP + LoRA | N/A (8 GPUs) | ~98% | $28.72 | High |
324
+ | 8x H200 + EP + Full | MAYBE | 100% (if it works) | $28.72 | Very High |
325
+ | Beast server | NO | N/A | Free | N/A |
326
+
327
+ **Recommendation:** Use LoRA with Unsloth on the current 1x H200 pod. The quality
328
+ difference between LoRA rank-16 and full SFT is minimal for SFT tasks, and the
329
+ training will actually complete instead of being killed.
330
+
331
+ ---
332
+
333
+ ## Sources
334
+
335
+ - [ms-swift#6473: System RAM OOM with Qwen3-30B-A3B](https://github.com/modelscope/ms-swift/issues/6473)
336
+ - [DeepSpeed#2870: ZeRO-3 MoE unsupported](https://github.com/microsoft/DeepSpeed/issues/2870)
337
+ - [DeepSpeed#7156: Expert optimizer state partitioning request](https://github.com/deepspeedai/DeepSpeed/issues/7156)
338
+ - [DeepSpeed#2899: ZeRO-3 OOM with 30B model](https://github.com/microsoft/DeepSpeed/issues/2899)
339
+ - [DeepSpeed#7021: CPU offload OOM with ZeRO-3](https://github.com/deepspeedai/DeepSpeed/issues/7021)
340
+ - [DeepSpeed ZeRO-3 Documentation](https://deepspeed.readthedocs.io/en/latest/zero3.html)
341
+ - [RunPod GPU Pricing (confirms 276 GB RAM per H200)](https://www.runpod.io/pricing)
342
+ - [RunPod OOM Guide](https://www.runpod.io/articles/guides/avoid-oom-crashes-for-large-models)
343
+ - [RunPod Resource Selection](https://www.runpod.io/blog/avoid-pod-errors-runpod-resources)
344
+ - [Unsloth MoE Fine-tuning](https://unsloth.ai/docs/basics/faster-moe)
345
+ - [Qwen3.6-35B-A3B Specs](https://apxml.com/models/qwen36-35b-a3b)
346
+ - [Qwen3.5 MoE vs Dense Fine-tuning](https://medium.com/@ishaafsalman/qwen3-5-fine-tuning-in-2026-moe-vs-dense-b2d17de73a9e)
347
+ - [ms-swift Qwen3.5 Best Practices](https://swift.readthedocs.io/en/latest/BestPractices/Qwen3_5-Best-Practice.html)
348
+ - [Megatron Core MoE Training (arxiv:2603.07685)](https://arxiv.org/pdf/2603.07685)
349
+ - [DeepSeek Memory Analysis (arxiv:2502.07846)](https://arxiv.org/pdf/2502.07846)