24 पॉइंट द्वारा GN⁺ 2025-06-25 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • GPU में computation speed, memory access speed की तुलना में बहुत अधिक होती है, इसलिए memory hierarchy प्रदर्शन का bottleneck बनती है
  • Arithmetic Intensity (AI) के आधार पर workloads को memory-bound या compute-bound माना जाता है, और A100 GPU का threshold लगभग 13 FLOPs/Byte है
  • Performance optimization की मुख्य रणनीतियों में Fusion और Tiling शामिल हैं; Fusion अनावश्यक memory round-trips कम करता है, और Tiling data reuse को अधिकतम करता है
  • Synchronization, Coalesced Load, bank conflict resolution जैसी GPU hardware की संरचनात्मक विशेषताओं को समझना high-performance kernels लिखने के लिए महत्वपूर्ण है
  • Occupancy, thread divergence को कम करना, quantization जैसी अतिरिक्त बातों का भी वास्तविक प्रदर्शन पर बड़ा प्रभाव पड़ता है

GPU की compute और memory hierarchy

  • GPU में आम तौर पर arithmetic processing speed, memory bandwidth से कहीं अधिक होती है
  • उदाहरण के लिए, NVIDIA A100 लगभग 19.5 TFLOPS (32-bit floating point) प्रदर्शन देता है, जबकि memory bandwidth लगभग 1.5TB/s है
  • 4-byte data को पढ़ने के दौरान दर्जनों calculations किए जा सकते हैं, इसलिए data movement ही प्रदर्शन का bottleneck है
  • Global Memory (VRAM) वह धीमी off-chip memory है जहाँ सारा data मौजूद रहता है, और Streaming Multiprocessor(SM) computation संभालता है
  • हर SM में तेज on-chip Shared Memory(SRAM) होती है, जिसे program द्वारा directly managed cache की तरह इस्तेमाल किया जा सकता है
  • Thread execution की सबसे छोटी unit है, और हर thread के पास अलग register का set होता है
  • 32 threads मिलकर एक Warp बनाते हैं, और Block उन threads का grid होता है जो एक ही SM पर execute होते हैं

प्रदर्शन क्षेत्र: memory-bound बनाम compute-bound

  • Kernel का प्रदर्शन या तो memory-bound होता है (data movement speed से सीमित) या compute-bound (SM की computation क्षमता से सीमित)
  • Arithmetic Intensity(AI) को Total FLOPs / Total Bytes Accessed के रूप में परिभाषित किया जाता है, और यह एक महत्वपूर्ण metric है
  • Roofline model: x-axis पर AI और y-axis पर FLOPS/s वाले graph में kernel का वास्तविक प्रदर्शन दिखाया जाता है
    • यदि AI कम है और kernel memory-bound है, तो यह diagonal line (memory bandwidth ceiling) पर होगा
    • यदि AI अधिक है और kernel compute-bound है, तो यह horizontal line (peak compute ceiling) पर होगा
  • A100 का Ridge Point 19.5 TFLOPS / 1.5 TB/s ≈ 13 FLOPs/Byte है
  • AI बढ़ाने से प्रदर्शन बेहतर होता है, और kernel compute-bound क्षेत्र तक पहुँच सकता है

Arithmetic Intensity बढ़ाने की रणनीतियाँ

  • सरल मॉडल: एक thread, C[i,j] का एक मान compute करता है → AI = 0.25 (बहुत कम, memory-bound)
  • यदि thread 2x2 tile compute करे तब भी AI = 0.5 (अब भी कम)
  • AI बढ़ाने के लिए कई threads को block स्तर पर Shared Memory में बड़े tiles load करके data reuse अधिकतम करना चाहिए
  • Block के अंदर threads के सहयोग से AI > 13 तक बढ़ाया जा सकता है, जिससे compute-bound क्षेत्र में प्रवेश संभव होता है

overhead-bound स्थिति

  • CPU(host) द्वारा GPU को काम सौंपने की प्रक्रिया में overhead हो सकता है
  • यदि GPU kernels बहुत छोटे हों या बहुत अधिक संख्या में हों, तो GPU काम की प्रतीक्षा में idle रह सकता है
  • आधुनिक frameworks asynchronous execution का उपयोग करते हैं, जहाँ command streams को पहले से queue किया जाता है ताकि overhead कम हो

प्रदर्शन बढ़ाने की दो मुख्य रणनीतियाँ: Fusion और Tiling

Operator Fusion

  • साधारण operation chain, जैसे y = relu(x + 1) में यदि हर operation अलग kernel में चले, तो data को global memory तक बार-बार जाना पड़ता है
  • Fusion कई operations को एक kernel में जोड़ देता है, जिससे intermediate values को global memory में store किए बिना registers में process किया जाता है और केवल final result लिखा जाता है
  • उदाहरण: Triton, torch.compile Inductor जैसे JIT compilers इसे automate करते हैं

Tiling

  • Matrix multiplication जैसे जटिल operations में single-thread model का AI कम होता है
  • Tiles को block स्तर पर बाँटने के बाद, block के सभी threads मिलकर data tiles को Shared Memory में load करते हैं, जिससे बड़े पैमाने पर data reuse संभव होता है
  • Computation आम तौर पर Load(global -> Shared Memory) - Synchronize(सिंक) - Compute(गणना) के 3-step pattern का पालन करती है

Coalesced Load और vectorization

  • Global memory से Shared Memory में data लाते समय Coalesced Access महत्वपूर्ण है, जहाँ warp के 32 threads लगातार 128-byte region को access करते हैं
  • Vectorization (जैसे float4) से एक बार में कई data values load की जा सकती हैं, जिससे hardware resources की बचत और memory bandwidth का बेहतर उपयोग होता है
  • Data alignment आवश्यक है, और matrix में byte count का K मान 4 का multiple होना चाहिए ताकि दक्षता बनी रहे

Shared Memory banks और bank conflicts

  • Shared Memory 32 स्वतंत्र banks से बनी होती है, इसलिए warp के 32 threads को conflict से बचने के लिए अलग-अलग banks access करने चाहिए
  • Row-wise access में conflict नहीं होता, जबकि column-wise access में conflict (same bank access) हो सकता है
  • B tile को Shared Memory में transpose करके store करने की load-and-transpose रणनीति, computation के दौरान row access को प्राथमिकता देकर bank conflicts से बचाती है

तेज on-chip computation patterns

मूल रणनीति 1: एक thread, एक output compute करे

  • BLOCK_DIM=32 सीमा के तहत AI अधिकतम 8 तक जाता है, इसलिए compute-bound क्षेत्र में पहुँचना संभव नहीं

रणनीति 2: एक thread, कई outputs compute करे

  • BLOCK_DIM=16, TILE_DIM=64 पर एक thread 4x4 outputs compute करता है → AI=16
  • चूँकि AI>13 है, इसलिए A100 पर compute-bound प्रदर्शन हासिल किया जा सकता है
  • Shared Memory से float4 जैसी vectorized loads द्वारा efficient computation संभव है

Tiling की वास्तविक सीमा: tile quantization

  • यदि matrix size, tile size का multiple नहीं है, तो boundary blocks वास्तविक ज़रूरत से बड़े क्षेत्र के लिए compute करते हैं, जिससे अनावश्यक computation और padding पैदा होती है
  • Boundary threads guard conditions के जरिए अनावश्यक memory access रोकते हैं, लेकिन computation loop वैसा ही चलता रहता है, जिससे बेकार computation (जैसे C += A * 0) होती है

अतिरिक्त performance tuning factors

Occupancy और latency hiding

  • जब कोई warp memory read जैसी लंबी latency पर अटकता है, तो SM तुरंत दूसरे warp पर switch करके idle time घटाता है; इसे latency hiding कहते हैं
  • कई Thread Blocks को एक साथ assign करने पर उच्च occupancy से wait time कम किया जा सकता है
  • यदि block या tile size बहुत बड़ा हो जाए, तो resident blocks की संख्या घटती है, occupancy कम होती है और प्रदर्शन गिरता है

Thread divergence को कम करना

  • यदि warp के अंदर if-else branching होती है, तो दोनों paths को क्रम से execute करना पड़ता है, जिससे effective performance लगभग आधी रह जाती है
  • min, max जैसे branchless code के उपयोग से branching कम करनी चाहिए

Quantization

  • FP32 → FP16/BFP16 जैसी precision reduction करने पर memory movement और process होने वाले data की मात्रा, दोनों में 2x सुधार मिल सकता है
  • A100 पर FP16 computation 312 TFLOPS तक पहुँच सकती है, जो FP32 के 19.5 TFLOPS की तुलना में अधिकतम 16x performance है
  • Quantization से Roofline पर AI को दाईं ओर (memory efficiency) और ऊपर (peak compute performance) दोनों दिशाओं में बढ़ाया जा सकता है

समग्र सारांश

  • GPU प्रदर्शन की मूल सीमा memory bandwidth और on-chip compute क्षमता के असंतुलन से आती है
  • प्रदर्शन सुधार data reuse को अधिकतम करने वाले Tiling और intermediate memory traffic को घटाने वाले Fusion से हासिल किया जाता है
  • High-performance kernels लिखने और optimize करने के लिए hardware structure (warp, bank, coalesced access, synchronization) को समझना आवश्यक है
  • व्यवहारिक स्तर पर occupancy, divergence को कम करना, quantization जैसे अतिरिक्त तत्व वास्तविक speed पर सीधा प्रभाव डालते हैं
  • High-performance GPU computation design में सैद्धांतिक AI सुधार, hardware की विशेषताओं का उपयोग, और वास्तविक data layout तथा size handling जैसी कई बातों का संयुक्त विचार आवश्यक है

1 टिप्पणियां

 
GN⁺ 2025-06-25
Hacker News राय
  • इस बात को लेकर जिज्ञासा कि compiler level पर whole-program optimization वास्तव में कितनी अच्छी तरह हो रही है; यह भी महसूस होता है कि हर LLM architecture को एक-एक करके optimize करने का मौजूदा तरीका कुछ पीछे छूटा हुआ सा है

  • उसी 4070 पर llama.cpp और vllm चलाकर ज़्यादा prompts को batch में process करने की कोशिश का अनुभव साझा, batch 8 से llama.cpp बहुत धीमा हो गया; GPU utilization ठीक दिख रहा था, लेकिन असल में bottleneck था, जबकि vllm ने इसे काफ़ी बेहतर संभाला

    • vllm paged kv cache और GPU की पसंद के fully coalesced layout का उपयोग करता है, इसलिए batch के लिए optimized performance देता है; दूसरी ओर llama.cpp का flat layout single prompt के लिए अच्छा है, इसलिए batch स्थिति में L2 memory access pattern टूट जाता है और speed गिरती है

    • llama.cpp में kv tensor को [seq, head, dim] से [head, seq, dim] रूप में interleave करने पर, vllm जिस तरह fused attention kernel को data feed करता है, उसकी तर्ज़ पर काम हुआ और तुरंत लगभग 2x compute performance बढ़ने का अनुभव साझा किया गया

    • bottleneck का कारण GPU खुद नहीं था, बल्कि shared memory access और global read को कैसे design किया गया है, यह था; vllm ने ठीक इसी बिंदु पर layout बदलकर हमला किया

    • ऐसे bottleneck analysis में 2 दिन से ज़्यादा लगे, और सिर्फ GPU utilization graph देखकर यह समझ नहीं आया; ज़्यादातर बातें trial and error से पता चलीं

    • यह जिज्ञासा भी रखी गई कि क्या ऐसे experiments को और आसानी से, hot reload तरीके से, बार-बार दोहराने का कोई तरीका है

    • यह कहा गया कि GPU bottleneck नहीं था, लेकिन इशारा किया गया कि memory layout की inefficiency ने आख़िरकार GPU की compute efficiency को ही bottleneck बना दिया था

    • deepseek कर्मचारी द्वारा कल जारी nano-vllm project का ज़िक्र, जो सिर्फ 1200 lines का है लेकिन vanilla vllm से भी तेज़ performance रिकॉर्ड करने की ख़बर साझा की गई https://github.com/GeeeekExplorer/nano-vllm

    • पूछा गया कि क्या llama.cpp में बदले हुए layout को pull request के रूप में डाला गया है; राय दी गई कि 2x improvement सभी के लिए बड़ा फ़ायदा हो सकता है

    • ik_llama.cpp नाम के project को भी आज़माने की सिफ़ारिश https://github.com/ikawrakow/ik_llama.cpp

  • यह राय दी गई कि यह उपयोगी जानकारी वाला article है, और इसका विषय वे बातें हैं जिन्हें NVIDIA GPU architecture बनाते समय चुनता है; इस पर ज़ोर दिया गया कि दूसरी कंपनियों से अंतर को गलत न समझें

    • उदाहरण के लिए AMD Instinct MI300 में FP32 पर अधिकतम 160 TFLOPS और 6TB/s HBM3/3E bandwidth है, जिससे ridge-point बदल जाता है; यह A100 के 13 FLOPs/byte के मुक़ाबले दोगुना, यानी 27 FLOPs/byte है; बड़ी HBM memory (128~256GB) tiling depth और occupancy के बीच व्यावहारिक trade-off को भी बदल देती है, लेकिन ऐसे GPU महंगे हैं और CUDA support न होना भी एक trade-off है

    • यह राय भी दी गई कि जब तक AMD computing software पर ज़्यादा ध्यान नहीं देता, तब तक NVIDIA GPU ही प्रमुख मौजूदगी बनाए रखेंगे

  • स्पॉइलर के तौर पर इस बात पर ज़ोर कि असल में महत्वपूर्ण चीज़ GPU के काम करने का तरीका नहीं, बल्कि उसे machine learning computation में कैसे इस्तेमाल किया जाता है

    • यह आलोचना भी आई कि सामग्री लगभग CUDA के एक सामान्य summary जैसी है, और relu example व torch के उल्लेख को छोड़ दें तो machine learning से इसका बहुत गहरा संबंध नहीं है
  • यह राय कि contrast colors का उपयोग ज़रूर होना चाहिए, readability पर ज़ोर

    • font-weight: 300 इस्तेमाल करने का अनुभव साझा; कहा गया कि ज़्यादातर Mac designers font smoothing options के हिसाब से develop करते हैं, इसलिए आम तौर पर उसे "normal" जैसा दिखने के लिए set किया जाता है, लेकिन Mac पतले fonts को भी आधा-मोटा दिखाता है; इसलिए designers अक्सर और पतले fonts से "सामान्य" एहसास बनाने की प्रवृत्ति रखते हैं, संबंधित लिंक भी साझा किया गया https://news.ycombinator.com/item?id=23553486

    • यह अनुमान भी लगाया गया कि लेखक dark mode में edit और format कर रहा हो सकता है; यह भी कहा गया कि edge://flags/#enable-force-dark लगाने पर links अच्छी तरह दिखते हैं

    • यह भी कहा गया कि links और code block के भीतर comments पढ़ने में विशेष रूप से ज़्यादा मेहनत लगी; contrast बढ़ाने का सुझाव दिया गया, हालांकि content quality को बहुत उत्कृष्ट बताया गया

    • यह आलोचना कि वेबसाइट ने text पर alpha transparency इस्तेमाल करके contrast को गंभीर रूप से घटा दिया, जो एक बड़ी गलती है

  • यह सुझाव कि इस लेख के लिए असल में "Nvidia GPU के बारे में बुनियादी तथ्य" जैसा शीर्षक ज़्यादा उपयुक्त होगा; यह पृष्ठभूमि भी दी गई कि WARP शब्दावली आधुनिक Nvidia GPU की विशेषता है, और लगभग 2003 के Nvidia GPU सिर्फ video game rendering के hardware थे, इसलिए वे आज के general-purpose computing GPU से पूरी तरह अलग थे; निष्कर्ष में कहा गया कि पोस्ट की सामग्री हर GPU पर लागू होने वाली सामान्य व्याख्या नहीं है

  • इसे वास्तव में बहुत अच्छा introductory material बताते हुए धन्यवाद दिया गया; कहा गया कि AI PC खुद assemble करते समय GPU पर कई दिनों तक शोध किया था, और इस लेख ने ज़रूरी core points और high-value application areas, यानी generative AI, को अच्छी तरह समेट दिया, जिससे बहुत मदद मिली; खास तौर पर A100 GPU की memory hierarchy diagram को बहुत उपयोगी बताया गया

  • ASCII diagram के इस्तेमाल पर हैरानी