हमेशा की तरह, यह आसपास के कोड के संदर्भ पर निर्भर करता है : उदाहरण के लिए आप x<<1
एक सरणी इंडेक्स के रूप में उपयोग कर रहे हैं ? या इसे किसी और चीज़ से जोड़ रहे हैं? या तो मामले में, छोटी शिफ्ट मायने रखता है (1 या 2) अक्सर तुलना में अधिक अनुकूलित कर सकते हैं अगर कंपाइलर सिर्फ शिफ्ट होने के लिए समाप्त होता है । संपूर्ण थ्रूपुट बनाम विलंबता बनाम फ्रंट-एंड अड़चनों के व्यापार का उल्लेख नहीं करना। एक छोटे टुकड़े का प्रदर्शन एक आयामी नहीं है।
एक हार्डवेयर शिफ्ट निर्देश संकलन करने के लिए एक कंपाइलर का एकमात्र विकल्प नहीं है x<<1
, लेकिन अन्य उत्तर ज्यादातर यही मान रहे हैं।
x << 1
x+x
अहस्ताक्षरित के लिए, और 2 के पूरक हस्ताक्षरित पूर्णांक के बराबर है । कंपाइलर हमेशा जानते हैं कि वे संकलन करते समय किस हार्डवेयर को लक्षित कर रहे हैं, इसलिए वे इस तरह से ट्रिक्स का लाभ उठा सकते हैं।
पर इंटेल Haswell , add
घड़ी प्रवाह क्षमता प्रति 4 है, लेकिन shl
एक तत्काल गिनती के साथ ही 2 घड़ी प्रवाह क्षमता प्रति है। (देख निर्देश तालिका के लिए http://agner.org/optimize/ और अन्य लिंक86टैग विकि)। SIMD वेक्टर शिफ़्ट 1 प्रति घड़ी (Skylake में 2) हैं, लेकिन SIMD वेक्टर पूर्णांक 2 प्रति घड़ी (Skylake में 3) हैं। विलंबता समान है, हालांकि: 1 चक्र।
एक विशेष शिफ्ट-बाय-वन एन्कोडिंग भी है shl
जहां गिनती को ओपोड में निहित किया गया है। ० have६ में केवल और बाद में cl
रजिस्टर द्वारा तत्काल-गणना की पारियां नहीं थीं । यह ज्यादातर राइट-शिफ्ट्स के लिए प्रासंगिक है, क्योंकि आप केवल लेफ्ट शिफ्ट्स के लिए जोड़ सकते हैं, जब तक कि आप मेमोरी ऑपरेंड शिफ्ट नहीं कर रहे हैं। लेकिन अगर बाद में मूल्य की आवश्यकता होती है, तो पहले एक रजिस्टर में लोड करना बेहतर होता है। लेकिन वैसे भी, shl eax,1
या add eax,eax
एक बाइट से कम है shl eax,10
, और कोड-आकार सीधे (डिकोड / फ्रंट-एंड अड़चनें) या अप्रत्यक्ष रूप से (एल 1 आई कोड कैश मिस) प्रदर्शन को प्रभावित कर सकता है।
अधिक आम तौर पर, छोटी पारी की गणना को कभी-कभी x86 पर एक संबोधित मोड में स्केल किए गए सूचकांक में अनुकूलित किया जा सकता है। इन दिनों आम उपयोग में आने वाले अधिकांश अन्य आर्किटेक्चर RISC हैं, और स्केल-इंडेक्स एड्रेसिंग मोड नहीं हैं, लेकिन x86 इसके लिए एक सामान्य पर्याप्त वास्तुकला है। (यदि आप 4-बाइट तत्वों की एक सरणी को अनुक्रमित कर रहे हैं, तो स्केल कारक को 1 के लिए बढ़ाने के लिए जगह है int arr[]; arr[x<<1]
)।
जिन परिस्थितियों x
में अभी भी मूल मूल्य की आवश्यकता है, उन स्थितियों में कॉपी + शिफ्ट की आवश्यकता आम है । लेकिन अधिकांश x86 पूर्णांक निर्देश जगह-जगह काम करते हैं। (जैसे add
या जैसे निर्देशों के लिए गंतव्य एक है shl
।) x86-64 सिस्टम V कॉलिंग कन्वेंशन रजिस्टरों में आर्ग में गुजरता है, जिसमें पहला एर्ग इन edi
और रिटर्न वैल्यू होता है eax
, इसलिए एक फ़ंक्शन जो रिटर्न x<<10
भी कंपाइलर एमिट कॉपी + शिफ्ट करता है। कोड।
LEA
अनुदेश आप बदलाव और जोड़ सकते हैं (क्योंकि यह को संबोधित मोड मशीन एन्कोडिंग का उपयोग करता, 0 से 3 की एक पारी गिनती के साथ)। यह परिणाम को एक अलग रजिस्टर में रखता है।
gcc और clang दोनों इन कार्यों को उसी तरह अनुकूलित करते हैं, जैसा कि आप Godbolt कंपाइलर एक्सप्लोरर पर देख सकते हैं :
int shl1(int x) { return x<<1; }
lea eax, [rdi+rdi] # 1 cycle latency, 1 uop
ret
int shl2(int x) { return x<<2; }
lea eax, [4*rdi] # longer encoding: needs a disp32 of 0 because there's no base register, only scaled-index.
ret
int times5(int x) { return x * 5; }
lea eax, [rdi + 4*rdi]
ret
int shl10(int x) { return x<<10; }
mov eax, edi # 1 uop, 0 or 1 cycle latency
shl eax, 10 # 1 uop, 1 cycle latency
ret
2 घटकों के साथ LEA में हाल ही के इंटेल और AMD CPU पर 1 चक्र विलंबता और 2-प्रति-घड़ी थ्रूपुट है। (सैंडब्रिज-परिवार और बुलडोजर / रायज़ेन)। इंटेल पर, यह केवल 1 प्रति घड़ी थ्रूपुट है जिसमें 3 सी विलंबता है lea eax, [rdi + rsi + 123]
। (संबंधित: Collatz अनुमान के परीक्षण के लिए मेरे हाथ से लिखे गए विधानसभा की तुलना में यह C ++ कोड अधिक तेज़ क्यों है? इस बारे में विस्तार से बताया गया है।)
वैसे भी, कॉपी + शिफ्ट में 10 अलग mov
निर्देश की जरूरत होती है । यह कई हालिया सीपीयू पर शून्य विलंबता हो सकता है, लेकिन यह अभी भी फ्रंट-एंड बैंडविड्थ और कोड आकार लेता है। ( क्या x86 का MOV वास्तव में "मुक्त" हो सकता है? मैं इसे क्यों नहीं पुन: पेश कर सकता हूं? )
यह भी संबंधित: x86 में केवल 2 लगातार leal निर्देशों का उपयोग करके एक रजिस्टर को 37 से गुणा कैसे करें? ।
कंपाइलर आसपास के कोड को बदलने के लिए भी स्वतंत्र है इसलिए वास्तविक शिफ्ट नहीं है, या यह अन्य ऑपरेशन के साथ संयुक्त है ।
उदाहरण के लिए उच्च बिट को छोड़कर सभी बिट्स की जांच करने के if(x<<1) { }
लिए एक and
का उपयोग कर सकते हैं । X86 पर, आप एक test
निर्देश का उपयोग करेंगे , जैसे test eax, 0x7fffffff
/ के jz .false
बजाय shl eax,1 / jz
। यह अनुकूलन किसी भी शिफ्ट काउंट के लिए काम करता है, और यह उन मशीनों पर भी काम करता है, जहाँ बड़ी-बड़ी पारियाँ धीमी होती हैं (जैसे पेंटियम 4), या गैर-मौजूद (कुछ माइक्रो-कंट्रोलर)।
कई आईएसएएस में केवल स्थानांतरण से परे बिट-हेरफेर निर्देश हैं। जैसे कि पावरपीसी में बहुत सारे बिट-फिल्ड एक्सट्रैक्ट / इन्सर्ट निर्देश हैं। या एआरएम के पास किसी अन्य अनुदेश के हिस्से के रूप में स्रोत ऑपरेंड की शिफ्ट्स हैं। (इसलिए शिफ्ट / रोटेट निर्देश move
शिफ्ट किए गए स्रोत का उपयोग करते हुए सिर्फ एक विशेष रूप है ।)
याद रखें, C विधानसभा भाषा नहीं है । जब आप कुशलता से संकलन करने के लिए अपने स्रोत कोड को ट्यून कर रहे हों तो हमेशा अनुकूलित कंपाइलर आउटपुट देखें।