फोन पर लार्ज लैंग्वेज मॉडल्स को चलाना अब केवल एक रिसर्च एक्सपेरिमेंट नहीं रह गया है। यह अब एक शिपिंग रियलिटी है। फिर भी, जैसे ही आप एक टॉय डेमो से वास्तविक प्रोडक्ट की ओर बढ़ते हैं, लेटेंसी (latency) फिर से उभर कर सामने आती है। आपने मॉडल को ट्रिम किया, वेट्स को क्वांटाइज़ किया, फिर भी प्रीफिल फेज (prefill phase) धीमा रहता है। टोकन अटक जाते हैं। UI फ्रीज हो जाता है। दोष शायद ही कभी वहां आता है जहां उसे होना चाहिए।

Android डिवाइसेस पर, LLM प्रीफिल के दौरान कंप्यूट (compute) लगभग कभी भी बॉटलनेक (bottleneck) नहीं होता है। मेमोरी बैंडविड्थ (memory bandwidth) होती है। आधुनिक फ्लैगशिप SoCs शक्तिशाली GPU और NPU कोर्स के साथ आते हैं जो मेमोरी सबसिस्टम द्वारा डेटा दिए जाने की तुलना में कहीं अधिक तेज़ी से अरिथमेटिक (arithmetic) प्रोसेस कर सकते हैं। जब आप एक नेइव अटेंशन इम्प्लीमेंटेशन (naive attention implementation) को प्रोफाइल करते हैं, तो एक्जीक्यूशन यूनिट्स सैचुरेटेड (saturated) नहीं होती हैं। वे इंतज़ार कर रही होती हैं। DRAM का इंतज़ार।

आपका क्वांटाइज़्ड मॉडल अभी भी धीमा क्यों महसूस होता है

ऑन-डिवाइस इन्फरेंस (on-device inference) के लिए क्वांटाइजेशन अब पहला डिफ़ॉल्ट कदम बन गया है। वेट्स को FP16 से INT8 में छोटा करने से मॉडल का साइज़ आधा हो जाता है और स्टोरेज कम हो जाती है। यह मदद तो करता है, लेकिन यह अटेंशन लेयर की लेटेंसी को ठीक नहीं करता है। कारण सरल है: क्वांटाइजेशन आपके द्वारा स्टोर किए जाने वाले डेटा की मात्रा को कम करता है, लेकिन यह अटेंशन मैकेनिज्म द्वारा किए जाने वाले मेमोरी ट्रांजेक्शन (memory transactions) की संख्या को कम नहीं करता है।

टेक्स्टबुक तरीके से लागू किया गया एक स्टैंडर्ड मल्टी-हेड अटेंशन लेयर, हर लेयर के लिए DRAM तक तीन बार राउंड ट्रिप करता है। Query, Key, और Value मैट्रिसेस को मेन मेमोरी से पढ़ा जाता है, स्कोर कंप्यूट किए जाते हैं, और इंटरमीडिएट रिजल्ट्स को वापस लिखा जाता है। अरिथमेटिक बहुत मामूली है। डेटा मूवमेंट बहुत भारी है। Android पर, जहाँ पावर और थर्मल बजट सीमित होते हैं, यह पैटर्न मेमोरी बस (memory bus) को बुरी तरह प्रभावित करता है। प्रोसेसर प्रभावी रूप से एक ही पुल को पार करने के लिए तीन बार टोल दे रहा है।

यदि आप INT8 मॉडल्स शिप कर रहे हैं और सोच रहे हैं कि प्रीफिल स्टेप अभी भी प्रॉम्प्ट की लंबाई के साथ क्वाड्रेटिकली (quadratically) क्यों बढ़ता है, तो यही इसका उत्तर है। वेट्स छोटे हैं, लेकिन एक्टिवेशन ट्रैफिक (activation traffic) अभी भी बहुत अधिक है।

बॉटलनेक मेमोरी है, मैथ नहीं

समाधान को समझने के लिए, रूफलाइन (roofline) को देखें। Snapdragon 8 Gen 3 और Dimensity 9300 जैसे चिप्स पर मोबाइल GPUs और NPUs का थ्योरिटिकल कंप्यूट थ्रूपुट (theoretical compute throughput) उनके LPDDR5X इंटरफेस की क्षमता से कहीं अधिक है। एक नेइव अटेंशन कर्नेल (naive attention kernel) में, प्रत्येक हेड Q और K के प्रोडक्ट पर सॉफ्टमैक्स (softmax) कंप्यूट करता है, और फिर उसे V से गुणा करता है। प्रत्येक इंटरमीडिएट स्कोर मैट्रिक्स ग्लोबल मेमोरी में मटेरियलाइज़ (materialize) हो जाता है। इसका मतलब है कि आपके पीक DRAM रीड्स सीक्वेंस की लंबाई के वर्ग (square) के साथ, यानी O(n²) के अनुपात में बढ़ते हैं। 1024-टोकन वाले प्रॉम्प्ट के लिए, मेमोरी ट्रैफिक पहले से ही इतना अधिक होता है कि वह एक्जीक्यूशन टाइम पर हावी हो जाता है।

कोर्स का उपयोग पूरी तरह से नहीं हो पाता क्योंकि वे लेटेंसी को छिपा नहीं सकते। आधुनिक प्रोसेसर पाइपलाइन्स को फुल रखने के लिए कैश (caches) पर निर्भर करते हैं। जब कोई एल्गोरिदम लगातार कैश मिस (cache miss) करता है और DRAM से डेटा फेच (fetch) करता है, तो एक्जीक्यूशन यूनिट्स खाली बैठी रहती हैं। क्वांटाइजेशन की कोई भी मात्रा कंप्यूट क्षमता और मेमोरी सप्लाई के बीच इस स्ट्रक्चरल मिसमैच (structural mismatch) को ठीक नहीं कर सकती।

टाइलिंग (Tiling) बैंडविड्थ को कैसे वापस लाती है

समाधान एक टाइलिंग स्ट्रैटेजी (tiling strategy) है जो इंटरमीडिएट स्कोर को DRAM में भेजने के बजाय ऑन-चिप SRAM में रखती है। यह वही विचार है जो Flash Attention को चलाता है, जिसे Android के कंप्यूट स्टैक के लिए अनुकूलित किया गया है। मेमोरी में एक पूरा n × n स्कोर मैट्रिक्स बनाने के बजाय, आप गणना को छोटे टाइल्स (tiles) में तोड़ देते हैं जो L1 कैश के अंदर फिट हो सकें। आप लोकल सॉफ्टमैक्स स्टैटिस्टिक्स (local softmax statistics) कंप्यूट करते हैं, रनिंग मैक्स वैल्यूज़ और नॉर्मलाइजेशन सम्स (normalization sums) को इकट्ठा करते हैं, और केवल अंतिम वेटेड आउटपुट को ही वापस मेमोरी में लिखते हैं।

यह बैंडविड्थ कॉम्प्लेक्सिटी (bandwidth complexity) को बदल देता है। पीक DRAM रीड्स O(n²) से घटकर O(n) हो जाते हैं, क्योंकि अब आपको मेन मेमोरी के माध्यम से पूरे स्कोर मैट्रिसेस को ले जाने की आवश्यकता नहीं होती है। सारा भारी काम एक्जीक्यूशन यूनिट्स के ठीक बगल में SRAM के अंदर होता है।

एक ठोस उदाहरण के लिए, 64 का टाइल साइज़ और 128 का हेड डायमेंशन (head dimension) मान लें। स्कोर टाइल 16 KB जगह घेरता है। यह फुटप्रिंट Snapdragon 8 Gen 3 और Dimensity 9300 जैसे वर्तमान फ्लैगशिप SoCs के L1 कैश के अंदर आसानी से समा जाता है। अरिथमेटिक लोकल रहता है। मेमोरी बस पर दबाव कम हो जाता है।

इसे Android पर लागू करना

एल्गोरिथमिक स्केच (algorithmic sketch) सीधा है, हालांकि विवरणों को सही ढंग से प्राप्त करना महत्वपूर्ण है।

अपने Query, Key को तोड़ें