VRAM measurements

Markdown: vram.md · Data: vram.json

Every VRAM measurement from the program. The cross_gpu section adds one-step probes on an AMD RX 7900 XTX (24 GB) and a second NVIDIA card. Training GPU: one 8 GB NVIDIA consumer card (an RTX 3070 Ti by today's nvidia-smi; torch reports 7.65 GiB total capacity). 'mode' is probe (one forward+backward on the single longest example, no optimizer step) or real (a full Trainer run; peak = torch.cuda.max_memory_allocated at the end). All training configs are 4-bit nf4 QLoRA on unsloth/gemma-3-4b-it with the recipe.json memory fixes unless noted.

GPU

Model
NVIDIA GeForce RTX 3070 Ti
Total MiB
8192
Usable GiB torch
7.65
Second card
NVIDIA GeForce RTX 3060 Ti, 8192 MiB
Driver
580.178.04
Note
Card names read on 2026-10-15 and mapped through CUDA_DEVICE_ORDER=PCI_BUS_ID, CUDA_VISIBLE_DEVICES=1 as set in every training script (the probe scripts document the same pin). This holds as long as neither card changed PCIe slot since September; the owner confirms both cards were in the box then. The 7.65 GiB figure is printed in every OOM message.
Measured on
Status
inferred

Measurements

Mode is real (a training run), probe (one forward and backward pass on the longest example) or load (model load only). Peak is the highest GPU memory seen. OOM means the run ran out of memory.

IDModeModelRankrsLoRAAlphaDropoutDoRASeq length (tokens)PeakStatusConfig, result and notesMeasured on
e2b-contended-loadloadunsloth/gemma-4-E2B-itConfig: Unsloth load_in_4bit, ~3.6 GB free (live models resident) | Result: loaded, then forward crashed (embedding offloaded to CPU)
e2b-two-gpu-splitloadunsloth/gemma-4-E2B-it4.38 GB on GPU0 + 3.17 GB on GPU1, zero CPU offloadConfig: Unsloth 4-bit, both GPUs visible | Result: forward crashed on a cross-device tensor in transformers' Gemma 4 forward
e2b-lora-paramsloadunsloth/gemma-4-E2B-itResult: LoRA 25.3M trainable of 5.15B total (0.49%); vendor guide says E2B LoRA needs 8-10 GB
g3-trial-loadloadunsloth/gemma-3-4b-it4.39 GBokConfig: Unsloth load_in_4bit, single GPU | Result: fits, zero CPU offload
g3-trial-stepprobeunsloth/gemma-3-4b-it5.59 GBokConfig: one forward+backward, example truncated to 2048 tokens
first-real-step-oomrealOOMoomConfig: plain transformers+peft, r16, full ~6300-token examples, before the memory fixes | Result: OOM: 7.39 GB used, 256 MB free, tried to allocate 248 MB
sdpa-forward-probeprobe7.16 GBokConfig: bare no-grad forward, mem_efficient SDPA forced, 6390-token example | Note: proved attention was not the bottleneck
logits-tensor-sizecomputedinferredConfig: [1, 6390, 262208] fp32 logits vs response span only | Result: 6.7 GB full vs 135 MB response-only | Note: arithmetic, not a profiler reading
checkpoint-boundary-climbprobeConfig: one forward across 34 layers with gradient checkpointing | Result: 4.5 GB to 6.99 GB without offload; 6.39 GB with save_on_cpu
lm-head-castloadConfig: pre-training VRAM, lm_head fp32 vs bf16 | Result: 4502 MB to 3221 MB
real-v1real164926 MBokDataset examples: 152
real-v2real165053 MBokNote: last resumed segment | Dataset examples: 490
real-v3real165040 MBokDataset examples: 696
real-v4real164889 MBokDataset examples: 59
real-v5real165117 MBokNote: last resumed segment | Dataset examples: 757
real-v6real645796 MBokDataset examples: 757
real-v7real1126647 MBokDataset examples: 757
real-dryrunreal1126634 MBokNote: 2-step dry run of the generic trainer | Dataset examples: 3
real-v8real1126643 MBokDataset examples: 1050
real-v9real1126643 MBokDataset examples: 1080
real-v10real645767 MBokDataset examples: 1080
real-v11real1126648 MBokDataset examples: 1167
real-v12real1126648 MBokEpochs: 2; Dataset examples: 1167
real-v13real112yes3271776653 MBokDataset examples: 1172
real-v14-run1real112yes0.1Result: died at step 184: CUDA illegal memory access (Xid 31 MMU fault) | Dataset examples: 1172
real-v14-run2real112yes0.1OOMoomResult: OOM at step 236: 6.98 GiB allocated, 394 MiB reserved-unallocated, 58 MiB free
real-v14-run3real112yes0.1OOMoomResult: OOM at step 239: 6.90 GiB allocated, 457 MiB reserved-unallocated, 78 MiB free
real-v15real96yes300.16629 MBokDataset examples: 1172; Predicted MB: 6793
real-v16real1126653 MBokDataset examples: 873
real-v17real1126658 MBokDataset examples: 1189
probe-0917-r96probe9670376977.6 MBokDataset: 2026-09-15 corpus; Logits to keep: 319; Pre step MB: 3847
probe-0917-r112probe11270377216.0 MBokDataset: 2026-09-15 corpus; Pre step MB: 3973
probe-0917-r128probe1287037OOMoomResult: OOM (tried 70 MiB, 48 MiB free) | Dataset: 2026-09-15 corpus; Pre step MB: 4098
probe-0918-r96probe9672887058.9 MBokDataset: 2026-09-18 unfiltered
probe-0918-r104probe10472887178.1 MBokDataset: 2026-09-18 unfiltered
probe-0918-r112probe1127288OOMoomResult: OOM (tried 72 MiB, 66 MiB free) | Dataset: 2026-09-18 unfiltered
probe-0918f-r112probe11269317054.8 MBokDataset: 2026-09-18 filtered; Logits to keep: 192
probe-dora-r112probe112yes7037OOMoomResult: OOM inside the DoRA weight-norm step (tried 100 MiB, 30 MiB free) | Pre step MB: 3985
probe-dora-r64probe64yes7037OOMoomResult: OOM inside the DoRA weight-norm step (tried 100 MiB, 76 MiB free) | Pre step MB: 3610
probe-0919f-r112probe11270686980.0 MBokNote: the calibration anchor | Dataset: 2026-09-19 filtered; Logits to keep: 73
sweep-r112-dropprobe112yes320.17177OOMoomResult: OOM | Dataset: 2026-09-21; Logits to keep: 237
sweep-r104-dropprobe104yes0.17177OOMoomResult: OOM
sweep-r96-dropprobe96yes0.171777127.6 MBok
sweep-r88-dropprobe88yes0.171777007.9 MBok
sweep-r80-dropprobe80yes0.171776890.3 MBok
sweep-r112-nodropprobe112yes3207177OOMoomResult: OOM, although v13 trained this exact config at a 6653 MB real peak

Cross-GPU probes

One-step probes of the training recipe on two GPU vendors. Each probe runs one forward and backward pass of unsloth/gemma-3-4b-it with LoRA on all 7 attention and MLP projections, on synthetic random tokens: seq_len tokens of input with the loss on the last response_len. Peak memory depends on shapes, not on what the tokens say, so the synthetic input stands in for the real worst-case example (checked on the RTX 3060 Ti: pre-step memory at rank 112 was 3,972.6 MB synthetic vs 3,972.55 MB real). No optimizer step, so a real training run's peak differs; see Calibration above. Memory fixes 'all' = chunked MLP, save_on_cpu activation offload, bf16 lm_head and logits_to_keep (see the recipe). Peak allocated is torch.cuda.max_memory_allocated; reserved adds the allocator's cache.

AMD Radeon RX 7900 XTX

Arch
gfx1100
Total MB
24560
OS
Windows 11 (build 26200)
Driver
AMD Adrenalin 26.7.1 (32.0.31035.1003)
Stack
ROCm 7.2.1 Windows SDK wheels, torch 2.9.1+rocm7.2.1, HIP 7.2.53211
Libraries
transformers 5.19.0, peft 0.21.2, accelerate 1.15.0, bitsandbytes 0.50.2
Python
3.12.10

NVIDIA GeForce RTX 3060 Ti

Total MB
8192
Usable GiB torch
7.66
OS
Ubuntu, Linux 7.0
Driver
580.178.04
Note
About 0.55 GB of the card was held by an Ollama process during the probes, so the rank 96 OOM is for about 7.1 GB free, not 7.66.
GPUQuantMemory fixesRankDropoutDoRASeq length (tokens)Pre-step MBPeak allocated MBPeak reserved MBStep sStatusMeasured on
RX 7900 XTX4bitall160.0no71773222.45721.36694.016.92ok
RX 7900 XTX4bitall640.0no71773597.76406.26752.014.13ok
RX 7900 XTX4bitall960.0no71773847.86884.87318.014.28ok
RX 7900 XTX4bitall1120.0no71773974.57194.57578.014.27ok
RX 7900 XTX4bitall1280.0no71774099.77395.97750.014.07ok
RX 7900 XTX4bitall1920.0no71774611.98383.48842.014.8ok
RX 7900 XTX4bitall2560.0no71775129.19338.39732.014.88ok
RX 7900 XTX4bitall960.1no71773847.87059.37520.014.64ok
RX 7900 XTX4bitall1120.1no71773974.57368.27794.015.46ok
RX 7900 XTX4bitall1280.1no71774099.77562.78038.014.21ok
RX 7900 XTX4bitall2560.1no71775129.19506.99890.014.99ok
RX 7900 XTX4bitall640.0yes71773633.98240.78564.019.53ok
RX 7900 XTX4bitall1120.0yes71774010.79018.89290.019.61ok
RX 7900 XTXbf16all160.0no71778327.110826.311818.014.7ok
RX 7900 XTXbf16all640.0no71778702.311509.512290.013.3ok
RX 7900 XTXbf16all1120.0no71779089.112317.512658.013.43ok
RX 7900 XTXbf16all2560.0no717710244.514443.214840.014.38ok
RX 7900 XTX4bitall1120.0no20483974.45585.87288.04.46ok
RX 7900 XTX4bitall1120.0no40963974.46207.97286.07.33ok
RX 7900 XTX4bitall1120.0no81923974.57486.07852.015.91ok
RX 7900 XTX4bitall1120.0no122883974.68917.29498.030.49ok
RX 7900 XTX4bitall1120.0no163843974.710523.611162.053.5ok
RX 7900 XTX4bitnone160.0no71774502.835829.237502.068.68spilled Finished only because Windows spilled past the 24 GB card into shared system memory; about 4x slower. Would not fit on this card under Linux. See gotcha G31.
RX 7900 XTX4bitnone1120.0no71775254.936581.338334.057.79spilled Finished only because Windows spilled past the 24 GB card into shared system memory; about 4x slower. Would not fit on this card under Linux. See gotcha G31.
RTX 3060 Ti4bitall160.0no71773222.15788.96076.017.28ok
RTX 3060 Ti4bitall640.0no71773597.36465.06826.017.26ok
RTX 3060 Ti4bitall960.0no71773847.56757.3oom Ollama held about 0.55 GB of this 8 GB card during the probe

Source: probe_kit/probe_vram.py and run_sweep.py (this site's probe kit)

Calibration

Method
Run the one-step probe on the worst-case example, then scale by the probe's measured over-estimate on a config that also has a real run.

Anchor

Probe MB
6980.0
Probe
the 2026-09-19-corpus probe at r112, no dropout
Real MB
6653
Real
v11/v13 r112 runs (6648/6653)
Probe overestimate pct
4.7

Prediction

Config
r96, rsLoRA alpha 30, dropout 0.1
Probe MB
7127.6
Predicted MB
6793
Actual MB
6629
Error pct
2.5
Derivation note
The chain script states the 4.7% over-estimate and the 6,793 MB prediction; it does not name the anchor probe. 6980 x (1 - 0.047) = 6652 and 7127.6 x 0.953 = 6793 match, so the anchor is taken to be the 2026-09-19-corpus r112 probe.
Value
replaced three crashes and about 11 GPU-hours that found the rank-112 dropout ceiling the hard way
Measured on
Status
inferred

Probe versus real gap

Observed
The probe over-estimates a real run's peak by roughly 4.7% (anchor) and up to >0.9 GB in the worst case: on the 21 Sep corpus the r112 no-dropout probe OOMs at ~7.6 GB in use, while v13 trained that exact config at 6653 MB.
What sources say
The chain script calls the probe 'a conservative upper bound, not a prediction'. No source explains the mechanism.
Probe design
one forward+backward on the single longest example in the corpus (worst-case activation memory), no optimizer step, model.train() set so gradient checkpointing is active, wrapped in save_on_cpu like the trainer
Length sensitivity
probe peaks rise with the longest example and its response span: r112 fits at 6931 tokens (7055 MB) and 7068 tokens (6980 MB, short response) but OOMs at 7177 and 7288 tokens
Measured on
Status
inferred

Rank cost

Real peak by rank MB
16: 4889-5117; 64: 5767-5796; 96 (+dropout): 6629; 112: 6643-6658
Probe cost per rank unit MB
Value: 15; Evidence: dropout sweep: r80 6890.3, r88 7007.9, r96 7127.6 MB, i.e. ~118-120 MB per 8 ranks; measured on
Real cost r64 to r112 MB
Value: about 850-880; Evidence: v6 5796 vs v7 6647 (same corpus); v10 5767 vs v9 6643 (same corpus); measured on
Measured on

Dropout memory

Claim
PEFT installs nn.Identity at lora_dropout=0 (allocates nothing) and nn.Dropout above 0, which saves a mask per call for backward across 7 target modules on 34 layers. Estimated cost about 350 MB of peak at rank 112, the whole remaining margin. The rate does not matter; the mask is allocated at any rate above 0.
Estimate MB
350
Note
350 MB is the program notes's estimate from comparing crashes against v13's 6653 MB peak, not a direct measurement. It corrects an earlier ledger entry that said dropout cost no extra VRAM.
Measured on
Measured update
Measured on 2026-10-15 at the one-step probe: dropout 0.1 adds 167 to 175 MB at ranks 96 to 256 (see Cross-GPU probes and finding F30), about half this estimate.
Status
inferred

Inference footprint

ModelGGUF size GBResident GB estimateNoteMeasured on
fine-tuned router, Q8_04.144.4
fine-tuned router, Q4_02.37
untuned model resident sizes are in baselines.json

Host RAM

Incident
The first rank probe lacked model.train(), so HF gradient checkpointing was silently off and save_on_cpu spilled every activation to pinned host RAM. It thrashed the box and later hit a 32 GB cgroup memory cap. Probes now run under systemd-run --scope -p MemoryMax=12G.
Measured on