जब GPU की VRAM खत्म हो जाए: क्या करें
GPU VRAM AI और ग्राफ़िक्स वर्कलोड की सबसे बड़ी रुकावट है। यूनिफ़ाइड मेमोरी, VRAM ऑफ़लोडिंग और NVMe स्वैपिंग अंदर से कैसे काम करते हैं, जानिए।

सबसे आम मशीन लर्निंग एरर Python traceback या shape mismatch नहीं है। वह है CUDA out of memory। या तो आपका मॉडल बहुत बड़ा है, batch size ज़रूरत से ज़्यादा है, या आपके intermediate activations GPU की VRAM में फिट नहीं हो रहे। RTX 4090 में 24 GB होता है। half precision में 70B parameter वाले मॉडल को लगभग 140 GB चाहिए। हिसाब नहीं बैठता, और बड़ा GPU खरीदने से समस्या बस टलती है — 80 GB वाला H100 भी सबसे बड़े मॉडल एक कार्ड में नहीं समा पाता।
लेकिन अगर आप सिस्टम RAM या यहाँ तक कि NVMe स्टोरेज का इस्तेमाल करके GPU मेमोरी को पारदर्शी तरीके से बढ़ा सकें तो? NVIDIA के Greenboost और इसी तरह के प्रोजेक्ट ठीक यही कर रहे हैं — मेमोरी हायरार्की (VRAM → सिस्टम RAM → SSD) का इस्तेमाल करके ऐसे वर्कलोड चलाए जा रहे हैं जो आपके हार्डवेयर में फिट नहीं होने चाहिए। परफ़ॉर्मेंस का समझौता असली है, लेकिन कई use cases के लिए हैरानी की बात है कि वह काफ़ी मैनेजेबल है। यह समझने के लिए पहले GPU मेमोरी हायरार्की को समझना ज़रूरी है।
GPU मेमोरी CPU मेमोरी से अलग क्यों है
CPU और GPU मेमोरी बुनियादी तौर पर अलग access patterns के लिए बनी हैं। CPU वर्कलोड latency-sensitive होते हैं — एक thread को किसी डेटा की ज़रूरत पड़ती है और वह आने तक रुका रहता है। CPU कैश इसी तरह के random access पैटर्न के लिए latency कम करने पर डिज़ाइन किए गए हैं।
GPU वर्कलोड throughput-sensitive होते हैं — हज़ारों threads में से हर एक को अपना डेटा चाहिए, और GPU latency छिपाने के लिए threads के बीच स्विच कर सकता है। GPU मेमोरी (डेटासेंटर GPU में HBM, कंज़्यूमर कार्ड में GDDR) bandwidth के लिए बनाई जाती है: हर सेकंड भारी मात्रा में डेटा पहुँचाना, भले ही किसी एक access में CPU कैश हिट से ज़्यादा समय लगे।
Memory bandwidth comparison (approximate):
RTX 4090 GDDR6X: 1,000 GB/s
H100 HBM3: 3,350 GB/s
DDR5 System RAM: 50 GB/s
PCIe 5.0 x16: 64 GB/s (theoretical max)
NVMe SSD: 7 GB/s
The bandwidth cliff between VRAM and system RAM is ~20x.
Between VRAM and NVMe it's ~140x.
This is why naive offloading to system RAM kills performance —
you're trying to feed a 1,000 GB/s appetite through a 50 GB/s straw.
यही bandwidth का अंतर है कि GPU मेमोरी को सीधे 'वर्चुअल' बनाना — CPU की तरह डिस्क की तरह सिस्टम RAM में डेटा पेज करना — सीधे-सीधे काम नहीं करता। CPU page faults को शायद 10 गुना धीमेपन के साथ झेल लेता है। GPU जब VRAM की जगह सिस्टम RAM से टकराता है तो bandwidth लगभग 20 गुना घट जाती है, और bandwidth-bound वर्कलोड (ज़्यादातर ML inference) के लिए इसका मतलब है 20 गुना धीमापन।
VRAM ऑफ़लोडिंग असल में कैसे काम करती है
चाल यह नहीं है कि सिस्टम RAM को धीमी VRAM समझा जाए। चाल यह है कि GPU को ज़रूरत पड़ने से पहले सिस्टम RAM से डेटा VRAM में prefetch कर दिया जाए, ताकि delay computation के पीछे छिप जाए। हर व्यावहारिक VRAM विस्तार तकनीक के पीछे यही मूल विचार है।
Neural network inference के दौरान computation layers के हिसाब से क्रमवार चलती है। जब GPU layer 5 प्रोसेस कर रहा होता है, तो उसे पता होता है कि अगली layer 6 है। एक स्मार्ट ऑफ़लोडिंग सिस्टम layer 5 के compute होते समय ही layer 6 के weights को सिस्टम RAM से VRAM में ट्रांसफ़र करना शुरू कर सकता है। अगर computation ट्रांसफ़र से ज़्यादा समय लेता है (बड़ी layers में अक्सर ऐसा होता है), तो ट्रांसफ़र पूरी तरह छिप जाता है — GPU कभी रुकता नहीं।
# Conceptual overlap of compute and transfer
# (simplified pseudocode)
def inference_with_offloading(model, input_data):
# Only 2 layers fit in VRAM at a time
# Rest are in system RAM
for i, layer in enumerate(model.layers):
# Start async transfer of NEXT layer while computing current
if i + 1 < len(model.layers):
async_transfer_to_gpu(model.layers[i + 1])
# Compute on current layer (GPU is busy, transfer happens in parallel)
output = layer.forward(input_data)
# Evict current layer from VRAM (it's done)
transfer_to_ram(layer)
# Wait for next layer transfer to complete (usually already done)
sync_transfer()
input_data = output
return output
यह pipeline तरीका inference के लिए अच्छा काम करता है, क्योंकि computation graph पहले से पता होता है — आपको ठीक-ठीक मालूम रहता है कि अगला कौन-सा weight चाहिए। Training मुश्किल है, क्योंकि backward pass को forward pass के activations चाहिए, जिससे data movement के पैटर्न ज़्यादा जटिल हो जाते हैं।
व्यवहार में अलग-अलग तरीके
CUDA Unified Memory
NVIDIA का CUDA Unified Memory VRAM और सिस्टम RAM दोनों को फैलाने वाला एक ही address space बनाता है। CUDA runtime access patterns के आधार पर पेजों को इनके बीच अपने-आप माइग्रेट करता है। जब GPU सिस्टम RAM के किसी पेज को access करता है, तो page fault होता है और वह पेज VRAM में माइग्रेट हो जाता है।
फ़ायदा यह है कि यह application के लिए पारदर्शी है। आपके CUDA कोड को डेटा कहाँ रखा है, यह मैनेज नहीं करना पड़ता। नुकसान यह है कि page faults महँगे होते हैं, और runtime की माइग्रेशन heuristics हमेशा application के access पैटर्न से मेल नहीं खातीं। Neural network inference जैसे predictable वर्कलोड के लिए explicit मैनेजमेंट automatic माइग्रेशन से बेहतर परिणाम देता है।
Layer-by-Layer ऑफ़लोडिंग
Hugging Face Accelerate, DeepSpeed ZeRO-Inference, और llama.cpp का --mmap flag explicit layer offloading लागू करते हैं। ये VRAM में सिर्फ़ active layers रखते हैं और बाकी को सिस्टम RAM या डिस्क से स्ट्रीम करते हैं। मॉडल का पूरा VRAM में फिट होना ज़रूरी नहीं — उसे बस एक-दो layers एक बार में फिट होने चाहिए।
इसी तरह कंज़्यूमर हार्डवेयर पर 70B मॉडल चलाना असल में काम करता है। 4-bit quantized 70B मॉडल को कुल मिलाकर लगभग 40 GB चाहिए, लेकिन कोई एक layer सिर्फ़ लगभग 1-2 GB की होती है। 24 GB VRAM और prefetching के साथ आप मॉडल को कम परफ़ॉर्मेंस असर के साथ चला सकते हैं — शायद पूरी तरह VRAM में फिट होने की तुलना में 30-50% धीमा।
NVMe को विस्तारित मेमोरी की तरह इस्तेमाल
सबसे आक्रामक तरीका NVMe SSDs को GPU मेमोरी के तीसरे स्तर के रूप में इस्तेमाल करता है। VRAM की तुलना में bandwidth बहुत कम है (~7 GB/s बनाम ~1,000 GB/s), लेकिन capacity लगभग असीमित है। 4 TB का NVMe ड्राइव $200 का आता है और एक साथ दर्जनों बड़े मॉडल रख सकता है।
NVIDIA के Greenboost जैसे प्रोजेक्ट इसे पारदर्शी तरीके से लागू करते हैं — GPU मेमोरी सिस्टम NVMe तक फैल जाता है, और smart prefetching से bandwidth की सीमा का असर कम किया जाता है। ऐसे inference वर्कलोड में जहाँ GPU का काफ़ी समय computation में जाता है (सिर्फ़ डेटा ढोने में नहीं), वहाँ NVMe की latency computation overlap से पूरी तरह छिप सकती है।
परफ़ॉर्मेंस वर्कलोड पर बहुत निर्भर करती है। Compute-bound operations (बड़े matrix multiplications) ट्रांसफ़र latency को अच्छी तरह छिपा लेते हैं। Memory-bound operations (बड़े context वाले attention mechanisms) नहीं छिपा पाते। व्यवहार में NVMe ऑफ़लोडिंग छोटे batch sizes वाले बड़े मॉडल के batch inference के लिए सबसे अच्छी है — ठीक वही उपयोग जो लोकल LLM inference के लिए चाहिए।
Apple का Unified Memory फ़ायदा
Apple Silicon की unified memory architecture पूरी तरह अलग तरीका अपनाती है: VRAM और RAM का भेद ही खत्म कर देती है। CPU और GPU एक ही physical memory pool साझा करते हैं। यहाँ 'ऑफ़लोडिंग' होती ही नहीं, क्योंकि कोई अलगाव नहीं है — GPU उसी मेमोरी को access करता है जिसे CPU इस्तेमाल करता है।
इससे bandwidth की समस्या खत्म नहीं होती — M-series की memory bandwidth (M4 Max के लिए ~400 GB/s) समर्पित GPU से कम है — लेकिन इससे वह PCIe बाधा खत्म हो जाती है जो discrete GPU ऑफ़लोडिंग को धीमा बनाती है। 128 GB unified memory वाला Mac बिना किसी ऑफ़लोडिंग ओवरहेड के 70B मॉडल को पूरी तरह GPU-accessible मेमोरी में रख सकता है।
समझौता यह है: जो वर्कलोड समर्पित GPU के VRAM में पूरी तरह फिट होते हैं, उनके लिए peak throughput कम है, लेकिन जो वर्कलोड फिट नहीं होते उनके लिए परफ़ॉर्मेंस कहीं बेहतर है। बड़े मॉडल inference में, जहाँ मॉडल discrete GPU की VRAM से बड़ा हो, Apple Silicon अक्सर ऑफ़लोडिंग वाले discrete GPU से तेज़ निकलता है, भले ही उसका कच्चा compute कम हो।
डेवलपर्स के लिए इसका मतलब
अगर आप GPU पर चलने वाले applications बना रहे हैं — ML inference, graphics, scientific computing — तो VRAM की सीमा आपके architecture फ़ैसलों को ठोस तरीकों से प्रभावित करती है।
- अपने working set का आकार जानें। GPU मेमोरी उपयोग की प्रोफ़ाइलिंग करें। Peak allocation नहीं — किसी भी समय का working set। अगर आपका peak 48 GB है लेकिन किसी एक operation को सक्रिय डेटा में 8 GB से ज़्यादा नहीं चाहिए, तो ऑफ़लोडिंग अच्छा काम करेगी। अगर किसी एक operation को सच में एक साथ 48 GB चाहिए, तो आपको बड़ा GPU चाहिए।
- access पैटर्न के आधार पर ऑफ़लोडिंग रणनीति चुनें। Sequential access (layer-by-layer inference) prefetching के साथ बढ़िया चलता है। Random access (बड़े contexts पर attention) नहीं। जानें कि आपका वर्कलोड किस पैटर्न पर चलता है।
- Quantization आमतौर पर ऑफ़लोडिंग से सस्ता पड़ता है। मॉडल को FP16 से INT4 में लाने पर मेमोरी 4 गुना कम होती है, और क्वालिटी पर असर मामूली होता है। सिस्टम RAM में ऑफ़लोड करने से latency बढ़ती है, क्वालिटी पर असर शून्य, लेकिन मेमोरी बचत सीमित। पहले quantization करें, फिर ऑफ़लोडिंग।
- Batch size आपका ट्यूनिंग नॉब है। बड़े batches को ज़्यादा मेमोरी चाहिए, लेकिन ओवरहेड बेहतर बँटता है। छोटे batches कम मेमोरी लेते हैं, पर प्रति सेकंड कम items प्रोसेस करते हैं। VRAM की सीमा के करीब हों तो batch size घटाना सबसे आसान उपाय है।
- मेमोरी फ़्रैग्मेंटेशन पर नज़र रखें। CUDA मेमोरी allocation समय के साथ VRAM को फ़्रैग्मेंट कर सकता है, खासकर variable-length inputs के साथ। हो सकता है कुल 8 GB खाली हो पर कोई एक लगातार 2 GB का ब्लॉक न मिले। PyTorch का
torch.cuda.memory_stats()फ़्रैग्मेंटेशन दिखाता है।torch.cuda.empty_cache()मदद कर सकता है, पर यह हर समस्या का हल नहीं है।
बड़े पैमाने के compute वर्कलोड में GPU मेमोरी हमेशा बाधा रहेगी। मॉडल VRAM से तेज़ी से बढ़ते हैं। लेकिन इस बाधा को संभालने के टूल — unified memory, smart offloading, multi-tier caching — इतने अच्छे होते जा रहे हैं कि 'VRAM में फिट नहीं होता' अब कोई कठोर दीवार नहीं रही। यह एक परफ़ॉर्मेंस समझौता है, और तेज़ी से, एक मैनेज करने लायक समझौता।


