ஒரு LLM நீண்ட பதிலை உருவாக்கும்போது, ஆரம்பத்தில் மின்னல் வேகத்தில் தொடங்கிய பிறகு அது ஏன் மெதுவாக நகர்கிறது என்று நீங்கள் எப்போதாவது யோசித்திருந்தால், நீங்கள் ஒரு வன்பொருள் முட்டுக்கட்டையை (hardware bottleneck) நேரலையில் பார்க்கிறீர்கள் என்று அர்த்தம். பெரும்பாலான டெவலப்பர்கள் தங்கள் Python குறியீடு, பிரேம்வொர்க் அல்லது மாடலின் அளவை இதற்கு காரணமாகக் கூறுகிறார்கள். அவர்கள் செயல்பாடுகளை ஆய்வு செய்கிறார்கள், ஆப்டிமைசர்களை மாற்றுகிறார்கள் மற்றும் முன்செயலாக்கத்திலிருந்து (preprocessing) மில்லி விநாடிகளைக் குறைக்க முயல்கிறார்கள். இவை எதுவும் உண்மையான சிக்கலைத் தீர்க்காது. வேகத்தின் வரம்பு உங்கள் மென்பொருளில் இல்லை. அது சிலிக்கானில் (silicon) உள்ளது.
ஒவ்வொரு பெரிய மொழி மாதிரியின் (LLM) inference பணியும் உங்கள் சர்வரில் உள்ள GPU-வின் இரண்டு இயற்பியல் பண்புகளைச் சார்ந்துள்ளது: அது எவ்வளவு வேகமாக எண்களைக் கணக்கிட முடியும், மற்றும் அந்த எண்களைக் கணக்கிடுவதற்காக எவ்வளவு வேகமாக அவற்றைத் தயார் நிலையில் கொண்டு வர முடியும்.
கணக்கீடு மலிவானது. தரவை நகர்த்துவது மலிவானது அல்ல
GPU சந்தைப்படுத்துதல் கணக்கீடுகளைப் (compute) பற்றிப் பேசுவதையே விரும்புகிறது. ஒரு வினாடிக்கு டிரில்லியன் கணக்கான மிதவைப்புள்ளி செயல்பாடுகள் (floating-point operations). அந்த எண்கள் வியக்கத்தக்கவை. ஆனால் கணக்கீடு என்பது கதையின் பாதி மட்டுமே. மற்ற பாதி நினைவக அலைவரிசை (memory bandwidth) ஆகும்; அதாவது தரவு, அதிக அலைவரிசை கொண்ட நினைவகத்திலிருந்து (high-bandwidth memory) கணக்கீட்டு மையங்களுக்கு (compute cores) செல்லும் வேகம்.
இந்த இரண்டு இணைப்புகளில் எது பலவீனமாக இருக்கிறதோ, அதற்கு மேல் ஒரு LLM-ஆல் வேகமாக இயங்க முடியாது. இருபது சிறந்த சமையல்காரர்களைக் கொண்ட ஒரு வணிக ரீதியான சமையலறையை கற்பனை செய்து பாருங்கள். அடுப்புகள் சூடாக உள்ளன, கத்திகள் கூர்மையாக உள்ளன, ஒவ்வொரு சமையல்காரரும் தயாராக உள்ளனர். ஆனால் காய்கறிகள் விநியோகம் ஒரு மிதிவண்டியில், ஒரு கூடையில் மட்டுமே வருகிறது. சமையலறை முடங்குகிறது. அதிக சமையல்காரர்களைச் சேர்ப்பது இதைச் சரிசெய்யாது. வேகமான அடுப்புகளை வாங்குவது இதைச் சரிசெய்யாது. முட்டுக்கட்டை அந்தச் சாலையில் தான் உள்ளது.
நவீன தரவு மைய GPU-களில், கணக்கீட்டு அலகுகள் (arithmetic units) மிகவும் சக்திவாய்ந்தவை, எனவே அவை பெரும்பாலும் தங்கள் கணக்கீடுகளை முடித்துவிட்டு, weights மற்றும் activations நினைவகத்திலிருந்து வந்து சேரும் வரை காத்திருக்கும் போது காலத்தை வீணடிக்கின்றன. இந்தச் சமநிலையின்மை உங்கள் குறியீட்டில் உள்ள பிழை அல்ல. இது சிப்கள் (chips) எவ்வாறு கட்டமைக்கப்படுகின்றன என்பதன் இயற்பியல் உண்மை. நினைவக அலைவரிசை, கணக்கீட்டுத் திறனுடன் (raw compute) இணைந்து வளரவில்லை. LLM-கள் இந்தச் சமநிலையின்மையால் பெரிதும் பாதிக்கப்படுகின்றன, ஏனெனில் அவற்றின் forward passes ஒவ்வொரு வெளியீட்டு டோக்கனுக்கும் (output token) ஒவ்வொரு அளவுருவையும் (parameter) அணுக வேண்டியுள்ளது.
ஏன் Prompts வேகமாகத் தோன்றுகின்றன மற்றும் Generation மெதுவாகத் தோன்றுகிறது
LLM inference இரண்டு வெவ்வேறு நிலைகளாகப் பிரிக்கப்பட்டுள்ளது, மேலும் அவை வன்பொருளை முற்றிலும் மாறுபட்ட வழிகளில் அழுத்துகின்றன.
Prefill என்பது உங்கள் prompt மாடலை முதலில் அணுகும்போது நிகழ்கிறது. அனைத்து டோக்கன்களும் ஒன்றாகவே வருகின்றன. GPU அவற்றை பெரிய மேட்ரிக்ஸ்-மேட்ரிக்ஸ் பெருக்கங்களைப் (matrix-matrix multiplications) பயன்படுத்தி இணையாகச் செயலாக்க முடியும். ஆயிரக்கணக்கான கணக்கீட்டு அலகுகள் ஒரே நேரத்தில் இயங்குகின்றன, மேலும் பணிச்சுமை அடர்த்தியாக உள்ளது. இந்த நிலை கணக்கீட்டுத் திறன் சார்ந்தது (compute-bound). தொடக்கத்தில் நீங்கள் காணும் அந்த திடீர் வேகமான மாற்றம்? அது GPU தான் செய்ய வேண்டிய வேலையைச் சரியாகச் செய்வதால் ஏற்படுகிறது.
Decode என்பது விஷயங்கள் கடினமாகும் இடம். மாடல் அடுத்த டோக்கனை உருவாக்கும்போது, அதை ஒவ்வொன்றாகவே செய்கிறது. இந்த நிலை மேட்ரிக்ஸ்-வெக்டர் செயல்பாடுகளைச் (matrix-vector operations) சார்ந்துள்ளது, இது GPU-வின் இணையாகச் செயலாக்கும் திறனில் மிகச் சிறிய பகுதியை மட்டுமே பயன்படுத்துகிறது. அதைவிட மோசமாக, ஒவ்வொரு புதிய டோக்கனும் முழு மாடல் weights-ஐயும் நினைவகத்திலிருந்து மீண்டும் ஏற்றும்படி GPU-வை கட்டாயப்படுத்துகிறது. கணக்கீட்டு
Quantization அலைக்கற்றை அகலம் (bandwidth) சிக்கலை நேரடியாகத் தாக்குகிறது. மாடல் எடைகள் (Model weights) பொதுவாக பதினாறு-பிட் மிதப்புப்புள்ளி (sixteen-bit floating-point) வடிவங்களில் சேமிக்கப்படுகின்றன. அவற்றை எட்டு-பிட் அல்லது நான்கு-பிட் முழு எண்களாக (integers) சுருக்குவதன் மூலம், பஸ் (bus) வழியாகப் பயணிக்கும் தரவின் அளவை நீங்கள் பாதியாக அல்லது அதற்கு மேலாகக் குறைக்கலாம். மாடல் ஒரு சீரான வெளியீட்டை உருவாக்க போதுமான துல்லியத்தைக் (precision) கொண்டிருக்க வேண்டும், ஆனால் நவீன post-training quantization முறைகள் தரத்தை அழிக்காமல் ஒரு மாடலின் நினைவகத் தேவையை (memory footprint) வியத்தகு முறையில் குறைக்க முடியும். தரவுப் பரிமாற்றம் குறைவாக இருந்தால், மெமரி கன்ட்ரோலரில் காத்திருக்கும் நேரமும் குறையும்.
FlashAttention இடைநிலை முடிவுகளை GPU-வின் வேகமான ஆன்-சிப் மெமரிக்குள் (on-chip memory) வைத்திருப்பதற்காக அட்டென்ஷன் மெக்கானிசத்தை (attention mechanism) மறுசீரமைக்கிறது. நிலையான அட்டென்ஷன் முறையானது பெரிய அட்டென்ஷன் மேட்ரிக்ஸ்களை (attention matrices) மெதுவான வெளிப்புற நினைவகத்தில் (external memory) எழுத வேண்டியிருந்தது, பின்னர் அவற்றை மீண்டும் படிக்க வேண்டியிருந்தது. FlashAttention கணக்கீட்டை SRAM-இல் பொருந்தக்கூடிய சிறிய டைல்களாக (tiles) பிரிக்கிறது, softmax மற்றும் ஸ்கேலிங் (scaling) படிகளை ஆன்-சிப்பிலேயே செய்கிறது, மேலும் இறுதி வெளியீடுகளை மட்டுமே அதிக அலைக்கற்றை அகலம் கொண்ட நினைவகத்திற்கு (high-bandwidth memory) எழுதுகிறது. இது முக்கிய நினைவகத்திற்குச் செல்லும் பயணங்களைக் குறைப்பதற்காகச் சற்று கூடுதல் கணக்கீட்டுத் திறனை (compute) மாற்றாகப் பயன்படுத்துகிறது, இது எப்போதும் ஒரு வெற்றிகரமான முடிவாகும்.
PagedAttention வேறு வகையான நினைவக வீணாவலைத் தீர்க்கிறது. டிகோடிங் (decode) செய்யும் போது, KV cache கணிக்க முடியாத வகையில் வளர்கிறது. பாரம்பரிய அமைப்புகள் ஒவ்வொரு வரிசைக்கும் (sequence) நிலையான, தொடர்ச்சியான நினைவகத் தொகுதிகளை (contiguous chunks) ஒதுக்குகின்றன, இதனால் சில வரிசைகள் முன்கூட்டியே முடிவடையும் போதும் மற்றவை விரிவடையும் போதும் பெரிய இடைவெளிகள் உருவாகின்றன. PagedAttention இயங்குதளங்களில் (operating systems) இருந்து விர்ச்சுவல் மெமரி (virtual memory) என்ற கருத்தைப்borrow செய்கிறது. இது KV cache பதிவுகளை நிலையான அளவு கொண்ட தொகுதிகளாகச் சேமிக்கிறது, அவற்றை தொடர்ச்சியற்ற முறையில் ஒதுக்கீடு செய்யலாம் மற்றும் ஒரு இண்டிரெக்ஷன் டேபிள் (indirection table) மூலம் இணைக்கலாம். இது ஒதுக்கப்பட்ட ஆனால் பாதி காலியான பஃபர்களுக்குள் (buffers) நினைவகம் வீணாவதைத் தடுக்கிறது மற்றும் பெரிய பேட்ச் அளவுகளை (batch sizes) அனுமதிக்கிறது, இது நினைவகப் பயன்பாட்டைத் துண்டாடல் சுமையால் (fragmentation overhead) பாதிக்கப்படாமல் பயனுள்ள வேலைகளில் ஈடுபடுத்துவதன் மூலம் ஒட்டுமொத்தத் த்ரப்புட்டை (throughput) மேம்படுத்துகிறது.
கேள்வியைத் மாற்றுங்கள்
தாமதம் (latency) அதிகரிக்கும் போது, பல குழுக்கள் ஒரு சிறிய மாடலுக்கு மாற வேண்டுமா அல்லது தங்கள் இன்ஃபரன்ஸ் சர்வரை (inference server) மீண்டும் எழுத வேண்டுமா என்று கேட்கிறார்கள். அந்த கேள்விகள் முக்கியம் தான், ஆனால் அவை இரண்டாம் நிலைத் தேவைகள். முதல் கேள்வி வன்பொருளைப் (hardware) பற்றியதாக இருக்க வேண்டும். உங்கள் GPU உண்மையில் கணக்கீடுகளைச் செய்வதில் பிஸியாக உள்ளதா அல்லது தரவுக்காகக் காத்திருக்கிறதா?
உங்கள் பயன்பாட்டு அளவீடுகளைப் (utilization metrics) பாருங்கள். GPU கணக்கீட்டு ஆக்கிரமிப்புடன் (compute occupancy) சேர்த்து நினைவக அலைக்கற்றை நிறைவுத்தன்மையையும் (memory bandwidth saturation) ஆய்வு செய்யுங்கள். டிகோடிங் செய்யும் போது அதிக நினைவகப் போட்டி (memory contention) மற்றும் குறைந்த கணிதத் தீவிரத்தன்மை (arithmetic intensity) ஆகியவற்றைக் கண்டால், அது உங்களுக்கு மாடல் கட்டமைப்பு (model architecture) சார்ந்த பிரச்சனை அல்ல. அது ஒரு இயற்பியல் (physics) பிரச்சனை. இதற்கான தீர்வு சுத்தமான Python குறியீட்டில் இருந்து கிடைக்காது. இது அதிகப்படியான பேட்ச்சிங் (batching), பைப்லைனில் வேகமாகச் செல்ல எடைகளை குவாண்ட்டைஸ் செய்தல், ஆன்-சிப்பிலேயே இருக்க அட்டென்ஷனை மறுசீரமைத்தல் மற்றும் இடம் தீர்ந்துவிடாமல் பெரிய பேட்ச்களைப் பொருத்த KV cache-ஐ நிர்வகித்தல் ஆகியவற்றிலிருந்து வரும்.
இன்ஃபரன்ஸை இந்தத் தெளிவுடன் பார்க்கத் தொடங்கினால், உகப்பாக்கம் (optimization) ஒரு இயந்திரவியல் செயல்முறையாகிவிடும். மாடலின் புத்திசாலித்தனம் விஷயங்களை மெதுவாக்குகிறது என்ற கட்டுக்கதைகளைத் துரத்துவதை நிறுத்திவிட்டு, வன்பொருள் உண்மையில் என்ன வழங்க முடியும் என்பதன் அடிப்படையில் பொறியியல் முடிவுகளை எடுக்கத் தொடங்குவீர்கள். இதுதான் வெறும் இயங்கும் அமைப்புகளுக்கும், அளவிடக்கூடிய (scale) உற்பத்தி அமைப்புகளுக்கும் (production systems) இடையிலான வேறுபாடு.
