2011-10-08 11 views
12

किसी ने मुझे कुछ साल पहले एक कमर को शून्य करने के लिए निम्न आदेश दिखाया।शून्य असाइनमेंट बनाम xor, दूसरा वास्तव में तेज़ है?

xor i,i 

उसने मुझे बताया कि यह केवल शून्य को असाइन करने से तेज़ है। क्या यह सच है? क्या कंपेलर ऐसी चीज करने के लिए कोड प्राप्त करने के लिए अनुकूलन करते हैं?

+0

संभावित डुप्लिकेट [xor reg का उपयोग करते हुए, reg mov reg पर लाभ देते हैं, 0?] (Http://stackoverflow.com/questions/1135679/does-using-xor-reg-reg-give-advantage-over -mov-reg-0) –

+0

['xor eax, eax' x86 asm में एक रजिस्टर को शून्य करने का सबसे अच्छा तरीका है (कई कारणों से, केवल कोड-आकार नहीं)] (http://stackoverflow.com/questions/33666617/सबसे अच्छा तरीका-से-सेट-ए-रजिस्टर-टू-शून्य-इन-x86-assembly-xor-mov-or-और), लेकिन सी स्रोत कोड में आपको हमेशा 'var = 0; 'और संकलक को आपके लिए xor का उपयोग करने दें। 'Var^= var' लिखें, क्योंकि इसका शून्य लाभ और कई संभावित नुकसान हैं (उदा। ऑप्टिमाइज़र को हरा देना, विशेष रूप से यदि var अनियमित है)। केवल एक टिप्पणी पोस्ट कर रहा है क्योंकि यह सवाल एएसएम बनाम कंपाइलर इनपुट के बारे में पूछने के बारे में उलझन में प्रतीत होता है। –

उत्तर

25

आप इस कोशिश कर सकते हैं अपने आप को इस सवाल का जवाब देखने के लिए:

movl $0,%eax 
    xor %eax,%eax 

तो एकत्रित न इकट्ठा:

as xor.s -o xor.o 
objdump -D xor.o 

और

0: b8 00 00 00 00   mov $0x0,%eax 
    5: 31 c0     xor %eax,%eax 

एक 32 बिट रजिस्टर के लिए mov अनुदेश है मिल 2.5 गुना बड़ा, राम से लोड करने में अधिक समय लगता है और उस कैश स्पेस का उपभोग करता है। दिन में लोड लोड अकेले एक हत्यारा था, आज मेमोरी चक्र समय और कैश स्पेस का तर्क दिया जा सकता है कि यह ध्यान देने योग्य नहीं है, लेकिन यह है कि यदि आपका कंपाइलर और/या कोड अक्सर ऐसा करता है तो आप कैश की हानि देखेंगे अंतरिक्ष और अधिक उत्खनन, और अधिक, धीमी, सिस्टम मेमोरी चक्र।

आधुनिक CPUs में, बड़े कोड-आकार भी डिकोडर्स को धीमा कर सकते हैं, शायद उन्हें प्रति चक्र अधिकतम x86 निर्देशों को डीकोड करने से रोक सकते हैं। (उदाहरण के लिए कुछ CPUs के लिए 16 बी ब्लॉक में 4 निर्देशों तक।)

performance advantages to xor over mov in some x86 CPUs (especially Intel's) that have nothing to do with code-size भी हैं, इसलिए x86 असेंबली में xor-zeroing हमेशा पसंद किया जाता है।


प्रयोगों का एक और सेट:

void fun1 (unsigned int *a) 
{ 
    *a=0; 
} 
unsigned int fun2 (unsigned int *a, unsigned int *b) 
{ 
    return(*a^*b); 
} 
unsigned int fun3 (unsigned int a, unsigned int b) 
{ 
    return(a^b); 
} 


0000000000000000 <fun1>: 
    0: c7 07 00 00 00 00  movl $0x0,(%rdi) 
    6: c3      retq 
    7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 
    e: 00 00 

0000000000000010 <fun2>: 
    10: 8b 06     mov (%rsi),%eax 
    12: 33 07     xor (%rdi),%eax 
    14: c3      retq 
    15: 66 66 2e 0f 1f 84 00 nopw %cs:0x0(%rax,%rax,1) 
    1c: 00 00 00 00 

0000000000000020 <fun3>: 
    20: 89 f0     mov %esi,%eax 
    22: 31 f8     xor %edi,%eax 
    24: c3      retq 

प्रमुखों क्या चर मैं, मैं अपने प्रश्न को जन्म दे सकता के रूप में XOR दिखाने का पथ नीचे।चूंकि आपने यह निर्दिष्ट नहीं किया है कि प्रोसेसर या आप किस संदर्भ का जिक्र कर रहे थे, यह पूरी तस्वीर को पेंट करना मुश्किल है। उदाहरण के लिए यदि आप सी कोड के बारे में बात कर रहे हैं, तो आपको समझना होगा कि कौन से कंपाइलर उस कोड पर करते हैं, और यह फ़ंक्शन में कोड पर भारी निर्भर करता है, अगर आपके xor के समय कंपाइलर के पास रजिस्टर में ऑपरेंड होता है और निर्भर करता है आपके कंपाइलर सेटिंग्स पर आपको एक्सोर ईएक्स, ईएक्स मिल सकता है। या संकलक इसे एक mov reg, 0 में बदलने के लिए चुन सकता है, या कुछ = 0 बदल सकता है; एक xor reg, reg के लिए।

कुछ और दृश्यों विचार करने के लिए:

यदि चर को पता रजिस्टर में पहले से ही है:

7: c7 07 00 00 00 00  movl $0x0,(%rdi) 

    d: 8b 07     mov (%rdi),%eax 
    f: 31 c0     xor %eax,%eax 
    11: 89 07     mov %eax,(%rdi) 

संकलक mov शून्य के बजाय XOR का चयन करेंगे। यदि आपने इस सी कोड को आजमाया है तो आपको यह मिलेगा:

void funx (unsigned int *a) 
{ 
    *a=*a^*a; 
} 

कंपाइलर इसे शून्य स्थान के साथ बदल देता है। बाइट्स की समान संख्या प्राप्त की गई, लेकिन एक की बजाय दो स्मृति की आवश्यकता होती है, और एक रजिस्टर जला दिया जाता है। और एक के बजाय निष्पादित करने के लिए तीन निर्देश। तो शून्य शून्य काफी बेहतर है।

अब अगर यह बाइट आकार के और एक रजिस्टर में है:

13: b0 00     mov $0x0,%al 
15: 30 c0     xor %al,%al 

कोड आकार में कोई अंतर नहीं। (लेकिन वे अभी भी अलग-अलग निष्पादित करते हैं)।


अब अगर आप एक और प्रोसेसर के बारे में बात कर रहे थे, मान लीजिए कि एआरएम

0: e3a00000 mov r0, #0 
    4: e0200000 eor r0, r0, r0 
    8: e3a00000 mov r0, #0 
    c: e5810000 str r0, [r1] 
    10: e5910000 ldr r0, [r1] 
    14: e0200000 eor r0, r0, r0 
    18: e5810000 str r0, [r1] 

आप XOR (अनन्य या, ईओआर) का उपयोग करके कुछ भी सहेजने न: एक अनुदेश एक अनुदेश दोनों लाए जाने और निष्पादन है । यदि आपके पास किसी रजिस्टर में चर का पता है तो किसी भी प्रोसेसर की तरह, रैम में कुछ एक्सपोर्ट करना। यदि आपको xor करने के लिए डेटा को किसी अन्य रजिस्टर में कॉपी करना है, तो आप अभी भी दो मेमोरी एक्सेस और तीन निर्देशों के साथ समाप्त हो गए हैं। यदि आपके पास प्रोसेसर है जो स्मृति के लिए स्मृति कर सकता है तो शून्य की चाल सस्ता है क्योंकि आपके पास केवल एक मेमोरी एक्सेस है और प्रोसेसर के आधार पर एक या दो निर्देश हैं।

असल में यह इससे भी बदतर है: eor r0, r0, r0required to have an input dependency on r0 (आउट-ऑफ-ऑर्डर निष्पादन सीमित है), स्मृति-आदेश नियमों के कारण। एक्सोर-शून्यिंग हमेशा शून्य उत्पन्न करता है, लेकिन केवल x86 असेंबली में प्रदर्शन में मदद करता है।


तो लब्बोलुआब यह, निर्भर करता है अगर तुम रजिस्टरों कोडांतरक में एक x86 सिस्टम पर कहीं भी 8088 से वर्तमान तक बात कर रहे हैं XOR है अक्सर तेजी क्योंकि अनुदेश छोटा होता है, तेजी से हासिल करेगा अगर कम कैश ले जाता है आपके पास एक है, अन्य कोड, आदि के लिए अधिक कैश छोड़ देता है। इसी तरह गैर-x86 परिवर्तनीय निर्देश लंबाई प्रोसेसर जिन्हें निर्देश में एन्कोड किए जाने वाले शून्य की आवश्यकता होती है, को भी लंबे निर्देश की आवश्यकता होगी, लंबे समय तक लाने का समय, कैश होने पर अधिक कैश का उपभोग किया जाएगा , आदि। तो xor तेज है (आमतौर पर, यह कैसे एन्कोड करता है पर निर्भर करता है)। यदि आपके पास सशर्त झंडे हैं तो आप बहुत खराब हो जाते हैं और आप उस झुकाव/xor को शून्य ध्वज सेट करना चाहते हैं, तो आपको सही निर्देश जला देना पड़ सकता है (कुछ प्रोसेसर पर mov झंडे को नहीं बदलता है)। कुछ प्रोसेसर के पास एक विशेष शून्य रजिस्टर होता है, यह सामान्य उद्देश्य नहीं होता है, जब आप इसका उपयोग करते हैं तो आपको एक शून्य मिल जाता है जिससे आप अधिक सामान्य उपयोग के मामले को बिना किसी निर्देश स्थान को एन्कोड कर सकते हैं या एक अतिरिक्त निर्देश चक्र को एक शून्य में तुरंत शून्य लोड कर सकते हैं । उदाहरण के लिए msp430, 0x1234 के एक कदम के लिए आपको दो शब्द निर्देश होंगे, लेकिन 0x0000 या 0x0001 को ले जाएं और कुछ अन्य स्थिरांक को एक ही निर्देश शब्द में एन्कोड किया जा सकता है।यदि आप राम में एक चर के बारे में बात कर रहे हैं, तो सभी प्रोसेसर को मेमोरी में डबल हिट होगा, रीड-संशोधित-दो मेमोरी चक्र लिखने के लिए निर्देशों को गिनती नहीं है, और यदि पढ़ा जाता है तो कैश लाइन भरने का कारण बनता है (लिखना तब होगा बहुत तेज़), लेकिन पढ़े बिना लिखने के लिए केवल कैश द्वारा सही हो सकता है और बहुत तेज़ी से निष्पादित हो सकता है क्योंकि प्रोसेसर समानांतर में चल रहा था, जबकि प्रोसेसर चल रहा था (कभी-कभी आपको वह प्रदर्शन लाभ मिलता है, कभी-कभी नहीं, हमेशा ट्यून करते हैं इसके लिए)। X86 और संभावित पुराने प्रोसेसर कारण हैं कि आप शून्य को स्थानांतरित करने के बजाय xoring की आदत क्यों देखते हैं। प्रदर्शन लाभ अभी भी उन विशिष्ट अनुकूलन के लिए है, सिस्टम मेमोरी अभी भी बहुत धीमी है और कोई भी अतिरिक्त मेमोरी चक्र महंगा है, वैसे ही किसी भी कैश को फेंक दिया जाता है महंगा है। हाफवे सभ्य कंपाइलर्स, यहां तक ​​कि जीसीसी, मैं एक xor i का पता लगाऊंगा, मैं i = 0 के समतुल्य होने के नाते और मामले के आधार पर बेहतर (औसत प्रणाली पर) निर्देश अनुक्रम के आधार पर चुनता हूं।

माइकल एब्राश द्वारा जेन ऑफ असेंबली की एक प्रति प्राप्त करें। अच्छी, उपयोग की गई प्रतियां एक उचित मूल्य ($ 50 से कम) पर उपलब्ध हैं, भले ही आप $ 80 प्रतियों के लिए जाएं, यह इसके लायक है। विशेष 8088 "साइकिल खाने वालों" से परे देखने की कोशिश करें और सामान्य विचार प्रक्रिया को समझें जो वह सिखाने की कोशिश कर रहा है। फिर आप अपने कोड को अलग-अलग कर सकते हैं, आदर्श रूप से कई अलग-अलग प्रोसेसर के लिए। जो आपने सीखा है उसे लागू करें ...

+0

उत्कृष्ट उत्तर! – stdcall

5

पुराने सीपीयू (लेकिन पेंटियम प्रो के बाद, टिप्पणियों के अनुसार) पर यह मामला होता था, हालांकि, अधिकांश आधुनिक CPU इन दिनों शून्य असाइनमेंट (रजिस्टरों और अच्छी तरह से गठबंधन चर के) के लिए विशेष गर्म पथ हैं। बराबर प्रदर्शन पैदा करना चाहिए। अधिकांश आधुनिक कंपाइलर आसपास के कोड के आधार पर दोनों के मिश्रण का उपयोग करते हैं (पुराने एमएसवीसी कंपाइलर हमेशा अनुकूलित बिल्ड में XOR का उपयोग करेंगे, और यह अभी भी XOR का उपयोग करता है, लेकिन कुछ परिस्थितियों में MOV reg,0 का भी उपयोग करेगा)।

यह माइक्रो ऑप्टिमाइज़ेशन का बहुत अधिक है, इसलिए टीबीएच, आप केवल वही कर सकते हैं जो आपको सबसे अच्छा साबित करता है, जब तक आपके पास रजिस्टर निर्भरताओं के कारण लगी हुई लूप नहीं होती है। हालांकि, यह ध्यान दिया जाना चाहिए कि XOR का उपयोग अधिकांश समय कम जगह लेता है, जो एम्बेडेड डिवाइसों के लिए बहुत अच्छा है या जब आप शाखा लक्ष्य को संरेखित करने का प्रयास करते हैं।

यह मानता है कि आप मुख्य रूप से x86 और इसके डेरिवेटिव का जिक्र कर रहे हैं, उस नोट पर @ पास्कल ने मुझे तकनीकी संदर्भों को प्रस्तुत करने का विचार दिया जो इसके आधार पर है। इंटेल ऑप्टिमाइज़ेशन मैनुअल में इसके दो सेक्शन हैं, अर्थात् 2.1.3.1 Dependancy Breaking Idioms और 3.5.1.7 Clearing Registers and Dependancy Breaking IdiomsXOR आधारित किसी भी प्रकार के पंजीकरण समाशोधन के लिए निर्देशों के आधार पर इन दो खंडों के मूलभूत वकील ने अपनी निर्भरता तोड़ने वाली प्रकृति (जो विलंबता को हटाती है) के कारण। लेकिन उन वर्गों में जहां कंडीशन कोड को संरक्षित करने की आवश्यकता है, MOV आईएनजी 0 एक रजिस्टर में पसंद किया जाता है।

+2

मेरे पास ** नहीं ** विचार है कि "शून्य असाइनमेंट के लिए हॉट पथ" से आपका क्या मतलब है। क्या आप एक संदर्भ प्रदान कर सकते हैं? एक साइड नोट के रूप में, 'xor reg, reg' पेंटियम प्रो पर' mov reg, 0' से धीमा था, क्योंकि प्रोसेसर ने सोचा था कि पूर्व में 'reg' पर निर्भरता थी। इससे पहले, प्रोसेसर के इस परिवार में आउट ऑफ़ ऑर्डर निष्पादन नहीं था, और इसके बाद, प्रोसेसर ने 'रेर' के पिछले मूल्य से स्वतंत्र 'xor reg, reg' को पहचानना सीखा। –

+1

@ पास्कल: "शून्य असाइनमेंट के लिए हॉट पथ" से मेरा मतलब था कि माइक्रो-कोड को न्यूनतम विलंबता के साथ ऐसा करने के लिए अनुकूलित किया गया है (जैसा कि आपने उल्लेख किया है निर्भरता को तोड़कर) – Necrolis

+5

सैंडी ब्रिज पर, एक्सोर-शून्यिंग विशेष-आधारित है और रजिस्टर द्वारा संभाला जाता है नामकरण, यह एक निष्पादन बंदरगाह का भी उपयोग नहीं करता है। मैंने 'mov reg, 0'' पर लागू होने वाली समान चालों के बारे में कुछ भी नहीं सुना है, लेकिन यदि वे मौजूद हैं तो यह अच्छा होगा, क्या आपके पास इसका स्रोत है? – harold

0

xor निर्देश छोटे और स्मृति बैंडविड्थ सीमाओं के लिए prefetch कतार के कारण 8088 (और कम से कम 8086) पर निश्चित रूप से सच था।

संबंधित मुद्दे