Simultaneous Multithreading कैसे काम करता है
(blog.codingconfessions.com)- SMT ऐसी तकनीक है जिसमें एक CPU core उसी cycle में कई threads के instructions issue करता है, ताकि सिर्फ instruction-level parallelism से खाली रह जाने वाले execution resources को बेहतर तरीके से भरा जा सके
- Intel का Hyper-Threading प्रति core दो threads का implementation है; यह architectural state को replicate करता है, जिससे operating system को एक physical core दो logical processors जैसा दिखता है
- असली implementation में सभी resources को सीधे दोगुना नहीं किया जाता; instruction pointer, ITLB और RAT को replicate किया जाता है, trace cache और TLB share होते हैं, जबकि uop queue, physical registers और reorder buffer को बराबर बाँटा जाता है
- performance gain workload पर निर्भर करता है: अगर दो threads cache के लिए compete करते हैं तो slowdown हो सकता है, जबकि data exchange करने वाले cooperative threads shared cache की वजह से तेज हो सकते हैं
- shared resources और speculative execution security vulnerabilities का कारण बन सकते हैं, इसलिए security-sensitive environments या maximum performance/lowest latency मांगने वाले workloads में SMT disable करना एक practical option बन जाता है
SMT की जरूरत क्यों है
- आधुनिक processors में सैकड़ों registers, कई load/store units और arithmetic units होते हैं, और इनका उपयोग करने के लिए pipelining, superscalar architecture, out-of-order execution जैसी instruction-level parallelism techniques इस्तेमाल की जाती हैं
- pipeline instruction execution को कई stages में बाँटती है और हर cycle में instruction को अगले stage में भेजती है; depth 5 pipeline में 5वें cycle के बाद अधिकतम 5 instructions साथ-साथ progress कर सकते हैं
- superscalar processor एक cycle में कई instructions issue कर सकता है, और हाल के Intel Core i7 processors एक cycle में 4 instructions issue कर सकते हैं
- वास्तविक programs में पर्याप्त independent instructions ढूँढना कठिन होता है, इसलिए execution resources खाली रहने का समय बनता है
- horizontal waste तब होता है जब एक thread के अंदर issue width भरने जितने independent instructions नहीं मिलते
- vertical waste तब होता है जब अगले instructions वर्तमान में execute हो रहे instruction पर depend करते हैं और एक cycle में कोई भी instruction issue नहीं हो पाता
- traditional multithreading हर cycle में सिर्फ एक thread का instruction issue करती है और अगले cycle में दूसरे thread पर switch करके vertical waste कम कर सकती है, लेकिन horizontal waste और context-switch overhead बने रहते हैं
- SMT context switch के बिना उसी cycle में कई threads के instructions issue करके execution resources को अधिक अनुपात में भरता है
- Intel का SMT implementation, Hyper-Threading, प्रति core दो threads तक सीमित है
Intel-शैली SMT की बुनियादी संरचना
- सामान्य non-SMT processor एक बार में सिर्फ एक thread के instructions execute कर सकता है
- हर thread की एक architectural state होती है, जिसमें register values, program counter, control registers आदि शामिल होते हैं
- दो threads के instructions साथ-साथ execute करने के लिए दोनों threads की state को साथ-साथ represent करना पड़ता है, इसलिए SMT implementation processor की architectural state को replicate करता है
- इस replication की वजह से एक physical processor operating system को दो logical processors जैसा दिखता है, और operating system हर एक पर threads schedule कर सकता है
- microarchitecture-level buffers और execution resources cost, power, chip area जैसे factors के आधार पर replicate, share या partition किए जाते हैं
- चर्चा का विषय मुख्य रूप से Intel का SMT implementation है, और यह Intel के 2002 white paper पर आधारित है
CPU microarchitecture के तीन हिस्से
- processor programmers को ISA public interface के रूप में देता है, और ISA में instruction set तथा instructions द्वारा इस्तेमाल किए जा सकने वाले registers शामिल होते हैं
- microarchitecture वह internal implementation है जो समान ISA support करने वाले processor models के बीच भी अलग हो सकता है
- आधुनिक processors की microarchitecture broadly तीन हिस्सों में बँटी होती है
- frontend: इसमें instruction control unit शामिल होता है, जो आगे execute होने वाले program instructions fetch और decode करता है
- backend: इसमें physical registers, arithmetic units, load/store units जैसे execution resources शामिल होते हैं और decoded instructions को resources allocate करके execution schedule करता है
- retirement unit: executed instructions के results को processor की architectural state में finally reflect करता है
frontend में SMT
- instruction pointer अगले fetch किए जाने वाले instruction का address track करता है
- SMT-capable processor दो programs के next instructions को independently track करने के लिए instruction pointers के दो sets रखता है
- trace cache हाल ही में decoded instruction traces store करता है, जिससे बार-बार execute होने वाले instructions की decoding cost और execution latency कम होती है
- दो logical processors इसे जरूरत के अनुसार dynamically share करते हैं
- अगर कोई thread ज्यादा instructions execute करता है, तो वह trace cache की ज्यादा entries occupy कर सकता है
- हर entry को thread information से tag किया जाता है ताकि दो threads के instructions अलग किए जा सकें
- trace cache access हर cycle में दो logical processors के बीच arbitrate किया जाता है
- trace cache miss होने पर frontend L1 instruction cache में उस address के instructions ढूँढता है, और L1 instruction cache miss होने पर उन्हें next-level cache या main memory से लाना पड़ता है
- L1 instruction cache data को virtual address से cache करता है, लेकिन main memory access के लिए physical address चाहिए होता है
- ITLB हाल में translated virtual addresses रखता है और virtual address को physical address में translate करता है
- SMT-capable processor में हर logical processor का अपना ITLB cache होता है
- main memory से instructions fetch करने वाला logic first come first served तरीके से काम करता है, लेकिन दोनों logical processors progress कर सकें इसलिए हर logical processor के लिए कम से कम एक request slot reserve करता है
- main memory से आए instructions decode होने से पहले एक छोटे streaming buffer में store होते हैं, और SMT-capable processor में यह buffer भी हर logical processor के लिए replicate होता है
- instructions fetch होने के बाद छोटे और सरल uop में decode किए जाते हैं
- uop, CPU frontend और backend की boundary की तरह काम करने वाली uop queue में जाते हैं
- uop queue दो logical processors के बीच बराबर share होती है, और यह static partitioning दोनों logical processors को independently progress करने देती है
backend में SMT
- backend uop queue से microinstructions लेता है और उन्हें execute करता है, लेकिन केवल original program order से बँधा नहीं रहता और out-of-order execution करता है
- पास-पास के program instructions अक्सर एक-दूसरे पर dependent होते हैं, और अगर main memory read जैसा high-latency काम हो तो dependent instructions को भी wait करना पड़ता है
- out-of-order execution engine पीछे के instructions को original order से पहले execute करके resource waste कम करता है
- allocator microinstructions के लिए जरूरी resources identify करता है और availability के हिसाब से allocate करता है
- एक cycle में वह एक logical processor के microinstructions को resources allocate करता है, और अगले cycle में दूसरे logical processor पर switch करता है
- अगर uop queue में सिर्फ एक logical processor के microinstructions हों या एक logical processor अपने हिस्से के सारे resources खत्म कर चुका हो, तो allocator सारे cycles दूसरे logical processor के लिए use करता है
- backend के मुख्य resources में replication, sharing और partitioning का mix होता है
- ISA level पर X86-64 में सिर्फ 16 general-purpose integer registers होते हैं, लेकिन microarchitecture level पर सैकड़ों physical integer registers और लगभग उतनी ही संख्या में floating-point registers होते हैं
- SMT-capable processor में physical registers दो logical processors में बराबर partition किए जाते हैं
- memory read/write operations में इस्तेमाल होने वाले load buffer और store buffer भी दो logical processors में बराबर partition किए जाते हैं
register renaming, scheduling और commit
- out-of-order execution के लिए backend register renaming करता है
- ISA level पर architectural registers कम होते हैं, इसलिए program instructions कई independent instructions में वही registers reuse करते हैं
- out-of-order execution engine original logical register को physical registers में से किसी एक से बदल देता है, जिससे पहले की execution के साथ parallel execution संभव होता है
- यह mapping register alias table, यानी RAT, में रखी जाती है
- दोनों logical processors के पास अपने-अपने architectural register sets होते हैं, इसलिए RAT की भी अपनी-अपनी copy होती है
- register renaming और allocator stages से गुजरे instructions ready queues में जाते हैं
- एक queue memory read/write instructions के लिए होती है, और दूसरी general instructions के लिए
- SMT-capable core में ये queues दो logical processors में बराबर partition होती हैं
- processor में कई instruction scheduler parallel रूप से होते हैं
- हर CPU cycle में ready queue के कुछ instructions scheduler को भेजे जाते हैं
- queue एक cycle में एक logical processor के instructions भेजती है और अगले cycle में दूसरे logical processor पर switch करती है
- scheduler logical processor की परवाह नहीं करता; जिन microinstructions के required operands और execution unit ready हैं, उन्हें तुरंत execution के लिए भेज देता है
- fairness के लिए scheduler queue के अंदर एक logical processor के active entries की संख्या पर limit होती है
- execution complete हुए instructions के results reorder buffer में जाते हैं
- instructions out-of-order execute हों, फिर भी processor की architectural state में उन्हें original program order में commit होना चाहिए
- SMT-capable core में reorder buffer दो logical processors में बराबर partition होता है
- retirement unit track करता है कि instructions architectural state में commit होने के लिए ready हैं या नहीं, और उन्हें सही program order में retire करता है
- SMT-capable core में यह हर logical processor के microinstructions के बीच बारी-बारी से process करता है
- अगर एक logical processor में retire करने के लिए microinstructions नहीं हैं, तो पूरी bandwidth दूसरे logical processor के लिए use होती है
- instruction retire होने के बाद L1 cache में write करना पड़ सकता है, और यह write selection logic भी हर cycle दो logical processors के बीच बारी-बारी से process करता है
memory subsystem और cache का असर
- data requests के virtual address को physical address में बदलने वाला TLB दो logical processors द्वारा जरूरत के अनुसार dynamically share किया जाता है
- TLB entries को logical processor id से tag किया जाता है ताकि दो logical processors की entries अलग की जा सकें
- हर CPU core का अपना private L1 cache होता है
- L2 cache microarchitecture के आधार पर private भी हो सकता है या cores के बीच shared भी
- L3 cache हो तो cores के बीच shared होता है
- cache logical processors की मौजूदगी को पहचानता नहीं है
- L1 cache और कुछ मामलों में L2 cache core-private होने के कारण, दो logical processors का data जरूरत के अनुसार साथ में रखता है
- अगर दो threads cache को aggressively use करते हैं, तो data conflicts और eviction हो सकते हैं, जिससे performance घट सकती है
- अगर दो threads उसी data set पर काम करते हैं, तो shared cache performance बढ़ा सकता है
performance और security में चयन के मानदंड
- SMT-capable core पर सिर्फ एक thread चलाने पर भी कई buffers और execution resources दो logical processors के बीच shared या partitioned अवस्था में रह जाते हैं, जिससे single-thread की potential performance घट सकती है
- unused logical processor पर operating system idle loop चलाता है, और यह loop भी वे resources consume कर सकता है जिनसे दूसरा logical processor maximum performance दे सकता था
- Intel Core processors में, जब core पर केवल एक thread चल रहा हो, तो resource sharing या partitioning नहीं दिखती; Intel इसे उस generation में introduced improvement के रूप में देखता है
- जब दो threads SMT-capable core के दो logical processors पर चलते हैं, तो cache access pattern performance को निर्धारित करता है
- अगर दो threads cache के लिए compete करते हैं, तो वे एक-दूसरे का data evict करके performance घटाते हैं
- अगर वे cooperative हों, जैसे एक thread द्वारा produced data दूसरा thread consume करता है, तो cache data sharing से performance बेहतर होती है
- अगर दो threads cache को competitively use नहीं करते, तो वे एक-दूसरे की performance को नुकसान पहुँचाए बिना CPU core resource utilization बढ़ा सकते हैं
- absolute maximum performance की जरूरत वाले programs में कई experts मानते हैं कि SMT बंद करना बेहतर है, ताकि single thread सभी resources use कर सके
- SMT के साथ security issues भी जुड़े होते हैं
- shared resources और speculative execution की वजह से sensitive data attacker को leak होने की possibility खुल सकती है
- Oracle Linux और Red Hat documents SMT-related security issue cases से जुड़े हैं
- general advice system में SMT को disable करने की दिशा में है
- यह rumor भी है कि Intel अपनी next-generation processor Arrow Lake में Hyper-Threading हटा सकता है
1 टिप्पणियां
Hacker News की रायें
SMT को बहुत सरल बनाकर समझें तो बात यह समझ आई कि जब कोई thread cache miss की वजह से रुका होता है, तब भी कीमती ALU को व्यस्त रखा जा सकता है
पुराने laptops में LPDDR धीमा था और cores भी कम थे, इसलिए इसकी value ज्यादा रही होगी, लेकिन आजकल कई बार scalable कामों से ज्यादा cores होते हैं, इसलिए value कम महसूस होती है
कभी-कभी महत्वपूर्ण thread के साथ उसी core पर काम schedule न करके cache contention से बचा जाता है, क्योंकि पता होता है कि single-thread performance ही bottleneck है
पहले DirectX 12 के multi-threaded rendering में Efficient/Performance cores और SMT cores को test किया था; i7-12700K पर सिर्फ P-core इस्तेमाल करने, P+SMT, और P+E+SMT में complex scene rendering time लगभग समान था. हालांकि Xbox Series X पर उसी test में SMT पर भी काम schedule करने पर थोड़ा तेज था
SMT तब चमकता है जब I/O का इंतजार हो या simple integer work हो. अगर दोनों threads FPU को saturate कर सकते हैं, तो CPU के internal data पर ownership दिखाने के लिए extra tagging के कारण आम तौर पर SMT धीमा हो जाता है
मुख्य बात cache miss से ज्यादा यह है कि write complete होने के दौरान core को दूसरा काम execute करने देता है
इसलिए कुछ code अच्छी तरह scale नहीं करते, और कुछ code लगभग linear speedup पाते हैं
शायद ऐसे processors को थोड़ी-थोड़ी देर झपकी लेने देना ही बेहतर हो
bus width, transfers per second, queuing, और bit transfer/storage per power बेहतर हुए हैं, लेकिन अगर program को ऐसा data चाहिए जो cache में नहीं है और prediction भी गलत निकला, तो आखिरकार RAM latency ही समस्या है
कहा जा रहा है कि Intel के next-generation Arrow Lake CPU Hyper-Threading, यानी SMT, को पूरी तरह हटा देंगे
Performance gain हमेशा application-dependent रहा है, इसलिए simplifying बेहतर हो सकता है
यह कब और कहाँ मायने रखता है, इस पर हाल की चर्चा यहाँ है: https://news.ycombinator.com/item?id=39097124
मैं अभी पक्का नहीं कह सकता कि हम उस point पर पहुँच चुके हैं, लेकिन Intel के P/E cores समान लक्ष्य की ओर एक alternative हैं और desktops पर, जहाँ single- और low-threaded काम ज्यादा होते हैं, काफी reasonable लगते हैं. Application optimization में SMT और E-core के फर्क को न संभालना भी valuable लगता है
दूसरी ओर AMD फिलहाल मोटे तौर पर homogeneous cores बनाए रखकर SMT जारी रखने की योजना में है. असल में कौन-सी strategy बेहतर है, यह applications के हिसाब से बहुत बदलता है, इसलिए सरल निर्णय लेना मुश्किल होगा
इसलिए thread count को
std::thread::hardware_concurrency() / 2 - 1, यानी cores की संख्या - 1, तक सीमित करने का फैसला किया.std::vectorके साथ काम कर रहा हूँजब यह अच्छा काम करता था, तब भी improvement बस double-digit percent भर था, और कुछ consecutive generations में यह और खराब रहा; समझ नहीं आता कि वे इसे बार-बार क्यों try करते रहे
ऐसे low-level CPU features कैसे काम करते हैं, यह पढ़ते समय हर बार हैरानी होती है
University में “Introduction to Computer Hardware” जैसी एक class ली थी, लेकिन असल में उसे “Introduction to CPU Design” कहना चाहिए था. Logic gates से adders, latches, flip-flops आदि बनाए, और semester के अंत तक gate level पर एक बहुत basic processor design कर सकते थे
लेकिन register renaming या out-of-order execution जैसी चीजें सोचकर बना लेना कल्पना से बाहर लगता है. क्या इन्हें भी gate level पर design किया जाता है? या कोई language और “compiler” होता है जो gates/transistors place कर देता है?
सभी assignments Verilog नाम की hardware description language में थे, जिससे कई elements को abstract करके लिखा जा सकता था
SMT के बारे में users की एक बड़ी आम गलतफहमी यह mental model है कि एक “real core” और उसके मुकाबले inferior एक core होता है
जितने भी observable aspects हैं, उनमें दोनों threads बराबर हैं
आखिर performance के हिसाब से देखें तो conceptually यह single-thread core के लगभग 1.25 cores, या application के अनुसार उसी ratio जैसा होने के ज्यादा करीब है
मुझे लगता है E-core लाने की मुख्य वजह power usage या performance से ज्यादा heat और die area है. इसलिए मैं हमेशा E-core के बिना वाला chip खरीदता हूँ, और मुझे लगता है वह बेहतर है
मुझे जिज्ञासा है कि इस तरह के विस्तृत technical लेख कैसे खोजे जाएँ
मैंने इसी सटीक विषय पर खोजा, लेकिन जैसा उम्मीद थी, यह end-user को लक्ष्य करने वाली तकनीक है, इसलिए search results में सिर्फ़ users के लिए लिखे लेख मिले जो कुछ भी ठीक से नहीं समझाते
हालांकि उनके दिए हुए URL ज़्यादातर hallucinated होते हैं
मैंने इसे दूसरे device पर नए Firefox private tab में भी check किया; tracking या caching को पूरी तरह रोकना तो संभव नहीं होगा, लेकिन मुझे यह काफ़ी reasonable approximation लगता है
यह व्याख्या कि “SMT चालू CPU core में कई buffers और execution resources को दो logical processors के बीच share करना पड़ता है, इसलिए SMT core पर सिर्फ़ एक thread चल रहा हो तब भी वह thread उन resources का उपयोग नहीं कर सकता और potential performance घट जाती है” अब सच नहीं है
SMT mode में ROB, fetch/decode bandwidth आदि को बाँटा जाता है, लेकिन मैंने कई SMT cores देखे हैं जो SMT न होने पर पूरा resource इस्तेमाल करने देते हैं
SMT का मुख्य उद्देश्य superscalar execution engine की utilization को अधिकतम करना है
सोच रहा हूँ कि क्या इस रुझान का मतलब है कि लोग मानने लगे हैं कि superscalar अब पहले जितना महत्वपूर्ण नहीं रहा
कुल मिलाकर अच्छा सारांश है, लेकिन कुछ जगहों पर थोड़ा मिला-जुला सा लगा
security से संबंधित न भी हो, तब भी insiders द्वारा इस्तेमाल किए जाने वाले practical know-how के बारे में और जानना चाहूँगा
बेचारे AMD के Bulldozer architecture को SMT न होने के लिए इतना कोसा गया था, और अब सब SMT से दूर जा रहे हैं
बेशक, मुझे पता है कि Bulldozer में SMT की कमी के अलावा भी बहुत ज़्यादा समस्याएँ थीं। असल में वह कुछ हद तक उलटी संरचना थी, जहाँ कई cores एक ही ALU जैसी चीज़ें share करते थे। फिर भी अगर वह performance को थोड़ा और बढ़ा पाता, तो शायद कहा जा सकता था कि उसने कुछ चीज़ों को पहले ही देख लिया था
Power10 फिलहाल core प्रति 8 threads वाले SMT8 को प्रभावी ढंग से support करता है, और उन्होंने वर्षों तक SMT-केंद्रित design को लगातार आगे बढ़ाया है, इसे देखते हुए लगता नहीं कि वे इसे छोड़ेंगे
ध्यान रखने लायक बात यह है कि GPU की compute units भी आम तौर पर प्रति CU 7–10 threads के स्तर का SMT इस्तेमाल करती हैं
यह तरीका latency छिपाने में मदद करता है
हर clock cycle में उपलब्ध कई threads में से उस thread का instruction चुना और शुरू किया जाता है जिसे ऐसे resources चाहिए जो busy नहीं हैं। अधिकांश GPU प्रति clock कई instructions शुरू नहीं करते, हालांकि शुरू होने के बाद कई instructions साथ-साथ आगे बढ़ सकते हैं। अगर वे प्रति clock कई instructions शुरू भी करें, तो उन्हें अलग-अलग instruction classes से होना पड़ सकता है जो अलग execution resources इस्तेमाल करती हैं, जैसे scalar instruction और vector instruction
SMT, यानी simultaneous multithreading, वह तरीका है जिसमें हर clock cycle में सभी threads से कई instructions एक साथ शुरू होते हैं, और वे instructions superscalar CPU की कई execution units के लिए प्रतिस्पर्धा करते हैं ताकि जितनी संभव हो उतनी execution units busy रहें। आधुनिक CPU के हर parallel execution unit—जैसे 6 integer adders में से प्रत्येक—के लिए, सभी simultaneous threads के instructions वाली queue से अलग से तय किया जाता है कि कौन सा instruction execute होगा