कंपाइलर अनुकूलन करने में वास्तव में अच्छे हैं switch
। हाल की gcc भी a में स्थितियों का एक गुच्छा अनुकूलित करने में अच्छा है if
।
मैंने गॉडबोल्ट पर कुछ परीक्षण मामले बनाए ।
जब case
मानों को एक साथ रखा जाता है, तो gcc, clang और icc सभी एक बिटमैप का उपयोग करने के लिए पर्याप्त स्मार्ट होते हैं ताकि यह जांचा जा सके कि क्या मान विशेष में से एक है।
उदाहरण के लिए gcc 5.2 -O3 switch
(और if
कुछ इसी तरह) के लिए संकलित करता है :
errhandler_switch(errtype): # gcc 5.2 -O3
cmpl $32, %edi
ja .L5
movabsq $4301325442, %rax # highest set bit is bit 32 (the 33rd bit)
btq %rdi, %rax
jc .L10
.L5:
rep ret
.L10:
jmp fire_special_event()
ध्यान दें कि बिटमैप तत्काल डेटा है, इसलिए इसे एक्सेस करने की कोई संभावित डेटा-कैश मिस या जंप टेबल नहीं है।
gcc 4.9.2 -O3 switch
एक बिटमैप के लिए संकलित करता है, लेकिन 1U<<errNumber
mov / Shift के साथ करता है । यह if
संस्करण को शाखाओं की श्रृंखला के लिए संकलित करता है ।
errhandler_switch(errtype): # gcc 4.9.2 -O3
leal -1(%rdi), %ecx
cmpl $31, %ecx # cmpl $32, %edi wouldn't have to wait an extra cycle for lea's output.
# However, register read ports are limited on pre-SnB Intel
ja .L5
movl $1, %eax
salq %cl, %rax # with -march=haswell, it will use BMI's shlx to avoid moving the shift count into ecx
testl $2150662721, %eax
jne .L10
.L5:
rep ret
.L10:
jmp fire_special_event()
ध्यान दें कि यह कैसे 1 से घटाता है errNumber
( lea
चाल के साथ उस ऑपरेशन को संयोजित करने के लिए)। यह 64 बिट तत्काल से बचने के लिए, एक 32 बिट तत्काल में बिटमैप फिट करने देता हैmovabsq
जो अधिक निर्देश बाइट लेता है ।
एक छोटा (मशीन कोड में) अनुक्रम होगा:
cmpl $32, %edi
ja .L5
mov $2150662721, %eax
dec %edi # movabsq and btq is fewer instructions / fewer Intel uops, but this saves several bytes
bt %edi, %eax
jc fire_special_event
.L5:
ret
(उपयोग करने में विफलता jc fire_special_event
सर्वव्यापी है, और संकलक बग है ।)
rep ret
पुराने AMD K8 और K10 (पूर्व-बुलडोजर) के लाभ के लिए, शाखा लक्ष्यों में और सशर्त शाखाओं का अनुसरण करने के लिए उपयोग किया जाता है: `rep ret` का क्या अर्थ है? । इसके बिना, शाखा भविष्यवाणी उन अप्रचलित सीपीयू पर भी काम नहीं करती है।
bt
(बिट परीक्षण) एक रजिस्टर arg के साथ तेज है। यह errNumber
बिट्स द्वारा ए-लेफ्ट-शिफ्टिंग के काम को जोड़ती है और ए कर रही है test
, लेकिन अभी भी 1 चक्र विलंबता और केवल एक इंटेल यूओपी है। यह अपने तरीके से बहुत अधिक CISC शब्दार्थ के कारण मेमोरी अरग के साथ धीमा है: "बिट स्ट्रिंग" के लिए मेमोरी ऑपरेंड के साथ, परीक्षण की जाने वाली बाइट का पता अन्य arg (8 से विभाजित) और isn के आधार पर गणना की जाती है '1, 2, 4 या 8byte chunk तक सीमित है, जिसे मेमोरी ऑपरेंड द्वारा इंगित किया गया है।
से Agner कोहरा के निर्देश टेबल , एक चर गिनती पारी अनुदेश एक की तुलना में धीमी है bt
हाल ही में इंटेल (2 के बजाय UOPs 1, और पारी और सब कुछ है कि के लिए आवश्यक नहीं है) पर।