आप शाखा की असफलता के शिकार हैं।
शाखा भविष्यवाणी क्या है?
एक रेल जंक्शन पर विचार करें:
विकिमीडिया कॉमन्स के माध्यम से मेकानिज्म द्वारा छवि । CC-by-SA 3.0 लाइसेंस के तहत उपयोग किया जाता है ।
अब तर्क के लिए मान लीजिए कि यह 1800 के दशक में वापस आ गया है - लंबी दूरी या रेडियो संचार से पहले।
आप एक जंक्शन के संचालक हैं और आप एक ट्रेन को आते हुए सुनते हैं। आपको पता नहीं है कि किस रास्ते से जाना है। आप ड्राइवर को यह पूछने के लिए ट्रेन रोकते हैं कि उन्हें कौन सी दिशा चाहिए। और फिर आप स्विच को उचित रूप से सेट करें।
ट्रेनें भारी हैं और उनमें बहुत जड़ता है। तो वे हमेशा के लिए शुरू करते हैं और धीमा करते हैं।
क्या कोई बेहतर तरीका है? आप अनुमान लगाएं कि ट्रेन किस दिशा में जाएगी!
- यदि आपने सही अनुमान लगाया है, तो यह जारी है।
- यदि आपने गलत अनुमान लगाया है, तो स्विच को फ्लिप करने के लिए कप्तान आपको रोक देगा, बैक अप और येल करेगा। फिर यह दूसरे पथ को पुनरारंभ कर सकता है।
अगर आप हर बार सही अनुमान लगाते हैं , तो ट्रेन को कभी भी रोकना नहीं पड़ेगा।
यदि आप अक्सर गलत अनुमान लगाते हैं , तो ट्रेन रुकने, बैकअप लेने और पुनः आरंभ करने में बहुत समय व्यतीत करेगी।
एक if-statement पर विचार करें: प्रोसेसर स्तर पर, यह एक शाखा निर्देश है:
आप एक प्रोसेसर हैं और आप एक शाखा देखते हैं। आपको पता नहीं है कि यह किस रास्ते पर जाएगा। आप क्या करते हैं? आप निष्पादन को रोकते हैं और पिछले निर्देशों के पूरा होने तक प्रतीक्षा करते हैं। फिर आप सही रास्ता जारी रखते हैं।
आधुनिक प्रोसेसर जटिल हैं और लंबी पाइपलाइनें हैं। इसलिए वे हमेशा के लिए "वार्म अप" और "धीमा" होते हैं।
क्या कोई बेहतर तरीका है? आप अनुमान लगाएं कि शाखा किस दिशा में जाएगी!
- यदि आपने सही अनुमान लगाया है, तो आप निष्पादित करना जारी रखते हैं।
- यदि आपने गलत अनुमान लगाया है, तो आपको पाइपलाइन को फ्लश करने और शाखा में वापस रोल करने की आवश्यकता है। तब आप दूसरे पथ को पुनरारंभ कर सकते हैं।
यदि आप हर बार सही अनुमान लगाते हैं , तो निष्पादन को कभी भी रोकना नहीं होगा।
यदि आप अक्सर गलत अनुमान लगाते हैं , तो आप बहुत समय रुकने, वापस आने और फिर से शुरू करने में बिताते हैं।
यह शाखा की भविष्यवाणी है। मैं मानता हूं कि यह सबसे अच्छा सादृश्य नहीं है क्योंकि ट्रेन केवल एक ध्वज के साथ दिशा का संकेत दे सकती है। लेकिन कंप्यूटर में, प्रोसेसर को यह नहीं पता होता है कि अंतिम क्षण तक एक शाखा किस दिशा में जाएगी।
तो आप रणनीतिक रूप से अनुमान कैसे लगा सकते हैं कि ट्रेन कितनी बार पीछे हटेगी और दूसरे रास्ते से नीचे जाएगी? आप पिछले इतिहास को देखें! यदि ट्रेन समय के 99% बाएं जाती है, तो आप अनुमान करते हैं कि बाएं। यदि यह वैकल्पिक है, तो आप अपने अनुमानों को वैकल्पिक करते हैं। यदि यह हर तीन बार एक तरह से जाता है, तो आप एक ही अनुमान लगाते हैं ...
दूसरे शब्दों में, आप एक पैटर्न की पहचान करने और उसका पालन करने की कोशिश करते हैं। यह कमोबेश शाखा भविष्यवक्ता कैसे काम करते हैं।
अधिकांश अनुप्रयोगों में अच्छी तरह से व्यवहार वाली शाखाएं होती हैं। इसलिए आधुनिक शाखा के भविष्यवक्ता आमतौर पर> 90% हिट दरों को प्राप्त करेंगे। लेकिन जब कोई पहचानने योग्य पैटर्न के साथ अप्रत्याशित शाखाओं का सामना करना पड़ता है, तो शाखा भविष्यवक्ता लगभग बेकार हैं।
आगे पढ़ें: "शाखा भविष्यवक्ता" विकिपीडिया पर लेख ।
जैसा कि ऊपर से संकेत दिया गया है, अपराधी यह है कि अगर बयान:
if (data[c] >= 128)
sum += data[c];
ध्यान दें कि डेटा 0 और 255 के बीच समान रूप से वितरित किया जाता है। जब डेटा को सॉर्ट किया जाता है, तो लगभग पुनरावृत्तियों का पहला भाग if-स्टेटमेंट में प्रवेश नहीं करेगा। उसके बाद, वे सभी इफ-स्टेटमेंट दर्ज करेंगे।
यह शाखा के भविष्यवक्ता के अनुकूल है क्योंकि शाखा कई बार एक ही दिशा में जाती है। यहां तक कि एक साधारण संतृप्त काउंटर दिशा को स्विच करने के बाद कुछ पुनरावृत्तियों को छोड़कर शाखा की सही भविष्यवाणी करेगा।
त्वरित दृश्य:
T = branch taken
N = branch not taken
data[] = 0, 1, 2, 3, 4, ... 126, 127, 128, 129, 130, ... 250, 251, 252, ...
branch = N N N N N ... N N T T T ... T T T ...
= NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT (easy to predict)
हालाँकि, जब डेटा पूरी तरह से यादृच्छिक होता है, तो शाखा भविष्यवक्ता बेकार हो जाता है, क्योंकि यह यादृच्छिक डेटा की भविष्यवाणी नहीं कर सकता है। इस प्रकार संभवतः लगभग 50% गलतफहमी होगी (यादृच्छिक अनुमान से बेहतर नहीं)।
data[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118, 14, 150, 177, 182, 133, ...
branch = T, T, N, T, T, T, T, N, T, N, N, T, T, T, N ...
= TTNTTTTNTNNTTTN ... (completely random - hard to predict)
तो क्या कर सकते हैं?
यदि कंपाइलर एक सशर्त चाल में शाखा का अनुकूलन करने में सक्षम नहीं है, तो आप कुछ हैक की कोशिश कर सकते हैं यदि आप प्रदर्शन के लिए पठनीयता का त्याग करने के लिए तैयार हैं।
बदलने के:
if (data[c] >= 128)
sum += data[c];
साथ में:
int t = (data[c] - 128) >> 31;
sum += ~t & data[c];
यह शाखा को समाप्त करता है और इसे कुछ बिटवाइज़ ऑपरेशंस से बदल देता है।
(ध्यान दें कि यह हैक मूल इफ-स्टेटमेंट के समतुल्य नहीं है। लेकिन इस मामले में, यह सभी इनपुट मानों के लिए मान्य है data[]
)
बेंचमार्क: कोर i7 920 @ 3.5 GHz
C ++ - विज़ुअल स्टूडियो 2010 - x64 रिलीज़
// Branch - Random
seconds = 11.777
// Branch - Sorted
seconds = 2.352
// Branchless - Random
seconds = 2.564
// Branchless - Sorted
seconds = 2.587
जावा - नेटबीन्स 7.1.1 JDK 7 - x64
// Branch - Random
seconds = 10.93293813
// Branch - Sorted
seconds = 5.643797077
// Branchless - Random
seconds = 3.113581453
// Branchless - Sorted
seconds = 3.186068823
टिप्पणियों:
- शाखा के साथ: सॉर्ट किए गए और अनसोल्ड डेटा के बीच बहुत बड़ा अंतर है।
- हैक के साथ: सॉर्ट और अनसोल्ड डेटा के बीच कोई अंतर नहीं है।
- C ++ के मामले में, हैक वास्तव में डेटा के छाँटे जाने पर शाखा के साथ तुलना में धीमा है।
अंगूठे का एक सामान्य नियम महत्वपूर्ण छोरों (जैसे इस उदाहरण में) में डेटा-निर्भर ब्रांचिंग से बचने के लिए है।
अपडेट करें:
X64 के साथ -O3
या -ftree-vectorize
पर GCC 4.6.1 एक सशर्त चाल उत्पन्न करने में सक्षम है। इसलिए सॉर्ट किए गए और अनसोल्ड डेटा के बीच कोई अंतर नहीं है - दोनों तेज हैं।
(या कुछ तेजी से: पहले से ही हल किए गए मामले के लिए, cmov
विशेष रूप से धीमा हो सकता है , खासकर अगर जीसीसी इसे महत्वपूर्ण पथ पर रखता है add
, विशेष रूप से इंटेल पर ब्रॉडवेल से पहले जहां cmov
2 चक्र विलंबता है: जीसीसी अनुकूलन ध्वज -O3 कोड -ओ 2 की तुलना में धीमा बनाता है )
वीसी ++ 2010 इस शाखा के लिए सशर्त चाल भी उत्पन्न करने में असमर्थ है /Ox
।
इंटेल C ++ कंपाइलर (ICC) 11 कुछ चमत्कारी करता है। यह दो छोरों को आपस में मिलाता है , जिससे अप्रत्याशित शाखा को बाहरी लूप में फहराया जाता है। इतना ही नहीं यह गलतफहमी के लिए प्रतिरक्षा है, यह कुलपति ++ और जीसीसी जो भी उत्पन्न कर सकता है उससे दोगुना है! दूसरे शब्दों में, ICC ने बेंचमार्क को हराने के लिए टेस्ट-लूप का फायदा उठाया ...
यदि आप इंटेल कंपाइलर को ब्रांचलेस कोड देते हैं, तो यह सही-सही सदिश करता है ... और शाखा (लूप इंटरचेंज के साथ) के समान ही तेज़ है।
यह दिखाने के लिए कि परिपक्व आधुनिक संकलक भी कोड को अनुकूलित करने की क्षमता में बेतहाशा भिन्न हो सकते हैं ...