क्या जीसीसी के लिए एक संकलक संकेत है कि शाखा भविष्यवाणी को हमेशा एक निश्चित तरीके से जाने के लिए मजबूर किया जाए?


118

इंटेल आर्किटेक्चर के लिए, क्या जीसीसी संकलक को कोड उत्पन्न करने का निर्देश देने का एक तरीका है जो हमेशा शाखा कोड को मेरे कोड में एक विशेष तरीके से मजबूर करता है? क्या इंटेल हार्डवेयर भी इसका समर्थन करता है? अन्य संकलक या हार्डवेयर के बारे में क्या?

मैं इसका उपयोग C ++ कोड में करूंगा जहां मुझे पता है कि मैं जिस केस को तेज चलाना चाहता हूं और धीमे पड़ने की परवाह नहीं करता जब दूसरी शाखा को तब भी ले जाने की जरूरत होती है जब उसने हाल ही में उस शाखा को लिया हो।

for (;;) {
  if (normal) { // How to tell compiler to always branch predict true value?
    doSomethingNormal();
  } else {
    exceptionalCase();
  }
}

Evdzhan मुस्तफा के लिए सवाल पर एक अनुसरण के रूप में, क्या संकेत पहली बार प्रोसेसर को निर्देश, सभी बाद की शाखा भविष्यवाणी, सामान्य रूप से कार्य करने का संकेत दे सकता है?


कुछ भी असामान्य होने पर (जो संकलक स्वतंत्र है) एक अपवाद भी फेंक सकता है
शेप

जवाबों:


9

C ++ 20 की संभावना और असंभावित विशेषताओं को मानकीकृत किया जाना चाहिए और पहले से ही g ++ 9 में समर्थित हैं । इसलिए जैसा कि यहां चर्चा है , आप लिख सकते हैं

if (a>b) {
  /* code you expect to run often */
  [[likely]] /* last statement */
}

निम्न कोड में उदाहरण के [[unlikely]]लिए यदि ब्लॉक में एक और ब्लॉक इनबिल्ड थैंक्स हो जाता है

int oftendone( int a, int b );
int rarelydone( int a, int b );
int finaltrafo( int );

int divides( int number, int prime ) {
  int almostreturnvalue;
  if ( ( number % prime ) == 0 ) {
    auto k                         = rarelydone( number, prime );
    auto l                         = rarelydone( number, k );
    [[unlikely]] almostreturnvalue = rarelydone( k, l );
  } else {
    auto a            = oftendone( number, prime );
    almostreturnvalue = oftendone( a, a );
  }
  return finaltrafo( almostreturnvalue );
}

Godbolt लिंक उपस्थिति / अनुपस्थिति की विशेषता से तुलना करता है


का प्रयोग क्यों [[unlikely]]में ifबनाम [[likely]]में else?
विलियमके

कोई कारण नहीं, बस इस नक्षत्र में समाप्त होने के बाद चारों ओर कोशिश कर रहा है कि विशेषता को कहाँ जाना है।
pseyfert

बहुत अच्छा। बहुत बुरा तरीका पुराने C ++ संस्करणों के लिए लागू नहीं है।
मैक्सिम Egorushkin

शानदार गॉडबोल्ट लिंक
लुईस केल्सी

87

जीसीसी __builtin_expect(long exp, long c)इस तरह की सुविधा प्रदान करने के लिए फ़ंक्शन का समर्थन करता है । आप यहां दस्तावेज देख सकते हैं

expउपयोग की गई शर्त कहां है और cअपेक्षित मूल्य है। उदाहरण के लिए आप के मामले में आप चाहते हैं

if (__builtin_expect(normal, 1))

अजीब वाक्यविन्यास के कारण यह आमतौर पर दो कस्टम मैक्रोज़ जैसे परिभाषित करके उपयोग किया जाता है

#define likely(x)    __builtin_expect (!!(x), 1)
#define unlikely(x)  __builtin_expect (!!(x), 0)

बस कार्य को आसान बनाने के लिए।

इस पर ध्यान दें:

  1. यह गैर मानक है
  2. एक संकलक / सीपीयू शाखा भविष्यवक्ता इस तरह की चीजों को तय करने में आपसे अधिक कुशल होने की संभावना रखते हैं ताकि यह समय से पहले सूक्ष्म अनुकूलन हो सके

3
क्या कोई कारण है कि आप एक मैक्रो दिखाते हैं और एक constexprफ़ंक्शन नहीं है ?
कोलंबो

22
@ कोलम्बो: मुझे नहीं लगता कि कोई constexprफ़ंक्शन इस मैक्रो को बदल सकता हैifमेरा मानना ​​है कि यह सीधे बयान में होना है। एक ही कारण assertएक constexprसमारोह कभी नहीं हो सकता है ।
मूविंग डक

1
@MooDDuck मैं सहमत हूँ, हालाँकि मुखर होने के और भी कारण हैं
शफीक यघमौर

7
@ कोलम्बो मैक्रो का उपयोग करने का एक कारण यह होगा क्योंकि यह C या C ++ के उन कुछ स्थानों में से एक है जहाँ एक मैक्रो फ़ंक्शन की तुलना में शब्दार्थ से अधिक सही है। समारोह केवल अनुकूलन की वजह से काम करने के लिए प्रकट होता है (यह है एक अनुकूलन: constexprकेवल मूल्य अर्थ विज्ञान, विधानसभा कार्यान्वयन विशेष को इनलाइन करने के बारे में बातचीत नहीं); कोड की सीधी व्याख्या (कोई इनलाइन) व्यर्थ है। इसके लिए किसी फ़ंक्शन का उपयोग करने का कोई कारण नहीं है।
लेउशेंको

2
@ लुशेंको मानते हैं कि __builtin_expectस्वयं एक अनुकूलन संकेत है, इसलिए यह तर्क देते हुए कि इसके उपयोग को सरल बनाने वाला एक तरीका अनुकूलन पर निर्भर करता है ... आश्वस्त नहीं है। इसके अलावा, मैंने constexprइसे पहली जगह में काम करने के लिए स्पेसियर नहीं जोड़ा , लेकिन इसे निरंतर अभिव्यक्तियों में काम करने के लिए। और हां, फ़ंक्शन का उपयोग करने के कारण हैं। उदाहरण के लिए, मैं एक प्यारा सा नाम जैसे मेरे पूरे नाम स्थान को प्रदूषित नहीं करना चाहता likely। मुझे इसका उपयोग करना होगा LIKELY, इस बात पर जोर देने के लिए कि यह एक मैक्रो है और टकराव से बचें, लेकिन यह केवल बदसूरत है।
कोलंबो

46

gcc में __builtin_expect (long exp, long c) ( जोर मेरा ) है:

आप शाखा भविष्यवाणी जानकारी के साथ संकलक प्रदान करने के लिए __builtin_expect का उपयोग कर सकते हैं। सामान्य तौर पर, आपको इस (-प्रोफाइल-आर्क्स) के लिए वास्तविक प्रोफ़ाइल फ़ीडबैक का उपयोग करना चाहिए, क्योंकि प्रोग्रामर यह अनुमान लगाने में बेहद खराब हैं कि उनके कार्यक्रम वास्तव में कैसा प्रदर्शन करते हैं । हालांकि, ऐसे अनुप्रयोग हैं जिनमें यह डेटा एकत्र करना कठिन है।

वापसी मूल्य एक्सप का मूल्य है, जो एक अभिन्न अभिव्यक्ति होना चाहिए। अंतर्निहित शब्दार्थ यह है कि यह अपेक्षित है कि exp == c। उदाहरण के लिए:

if (__builtin_expect (x, 0))
   foo ();

इंगित करता है कि हम फू को कॉल करने की उम्मीद नहीं करते हैं, क्योंकि हम उम्मीद करते हैं कि एक्स शून्य है। चूंकि आप विस्तार के लिए अभिन्न भावों तक सीमित हैं, इसलिए आपको निर्माण का उपयोग करना चाहिए जैसे कि

if (__builtin_expect (ptr != NULL, 1))
   foo (*ptr);

पॉइंटर या फ्लोटिंग-पॉइंट वैल्यू का परीक्षण करते समय।

दस्तावेज़ीकरण नोट्स के रूप में आपको वास्तविक प्रोफ़ाइल प्रतिक्रिया का उपयोग करना पसंद करना चाहिए और यह लेख इस बात का व्यावहारिक उदाहरण दिखाता है कि कैसे और कम से कम उनके मामले में इसका उपयोग करने पर सुधार हो रहा है __builtin_expect। यह भी देखें कि g ++ में प्रोफ़ाइल निर्देशित अनुकूलन का उपयोग कैसे करें?

हम भी kernal macros संभावना () और संभावना नहीं () जो इस सुविधा का उपयोग पर एक लिनक्स कर्नेल newbies लेख पा सकते हैं :

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

!!मैक्रो में प्रयुक्त नोट हम इसके लिए स्पष्टीकरण का उपयोग कर सकते हैं क्यों (हालत) के बजाय !! (हालत) का उपयोग क्यों करें?

सिर्फ इसलिए कि इस तकनीक का उपयोग लिनक्स कर्नेल में किया जाता है इसका मतलब यह नहीं है कि यह हमेशा इसका उपयोग करने के लिए समझ में आता है। हम इस सवाल से देख सकते हैं कि मैंने हाल ही में फंक्शन परफॉर्मेंस के बीच अंतर का जवाब दिया था जब कंपाइलिंग टाइम कॉन्सटेबल या वैरिएबल के रूप में पास किया गया था कि कई हैंड रोलेड ऑप्टिमाइजेशन तकनीक सामान्य केस में काम नहीं करती हैं। हमें यह समझने के लिए कोड को सावधानीपूर्वक प्रोफाइल करने की आवश्यकता है कि क्या कोई तकनीक प्रभावी है। कई पुरानी तकनीकें आधुनिक संकलक अनुकूलन के साथ भी प्रासंगिक नहीं हो सकती हैं।

ध्यान दें, हालांकि निर्मित पोर्टेबल क्लैंग नहीं हैं, यह __builtin_expect का भी समर्थन करता है

इसके अलावा कुछ आर्किटेक्चर पर इससे फर्क नहीं पड़ सकता है


लिनक्स कर्नेल के लिए पर्याप्त अच्छा क्या है जो C ++ 11 के लिए पर्याप्त नहीं है।
मैक्सिम इगोरुस्किन

@MaximEgorushkin नोट, मैं वास्तव में इसके उपयोग की अनुशंसा नहीं करता, वास्तव में gcc प्रलेखन जो मैं उद्धृत करता हूं, जो मेरा पहला उद्धरण है, उस तकनीक का उपयोग भी नहीं करता है। मैं कहूंगा कि मेरे उत्तर का मुख्य जोर इस मार्ग से नीचे जाने से पहले विकल्पों पर सावधानीपूर्वक विचार करना है।
शफीक यघमौर

44

नहीं वहाँ नहीं है। (कम से कम आधुनिक x86 प्रोसेसर पर।)

__builtin_expectअन्य उत्तरों में उल्लेख किया गया है कि जिस तरह से gcc असेंबली कोड को व्यवस्थित करता है। यह सीपीयू की शाखा भविष्यवक्ता को सीधे प्रभावित नहीं करता है । बेशक, कोड को पुन: व्यवस्थित करने के कारण शाखा की भविष्यवाणी पर अप्रत्यक्ष प्रभाव पड़ेगा। लेकिन आधुनिक x86 प्रोसेसर पर कोई निर्देश नहीं है जो सीपीयू को "यह शाखा है / नहीं लिया गया है" मान लेता है।

अधिक विस्तार के लिए यह प्रश्न देखें: Intel x86 0x2E / 0x3E उपसर्ग शाखा भविष्यवाणी वास्तव में उपयोग की गई है?

स्पष्ट होने के लिए, __builtin_expectऔर / या आपके कोड के प्रदर्शन में सुधार -fprofile-arcs कर सकते हैं , दोनों कोड लेआउट के माध्यम से शाखा के भविष्यवक्ता को संकेत देते हैं ( x86-64 विधानसभा के प्रदर्शन अनुकूलन देखें - संरेखण और शाखा भविष्यवाणी ), और साथ ही व्यवहार में सुधार भी "संभावना" कोड से "असंभावित" कोड को दूर रखकर।


9
यह गलत है। X86 के सभी आधुनिक संस्करणों पर, डिफ़ॉल्ट भविष्यवाणी एल्गोरिदम यह अनुमान लगाने के लिए है कि आगे की शाखाएं नहीं ली गई हैं और यह कि पिछड़ी शाखाएं हैं (देखें software.intel.com/en-us/articles/… )। तो अपने कोड को फिर से व्यवस्थित करके आप प्रभावी रूप से सीपीयू को संकेत दे सकते हैं । यह बिलकुल वैसा ही है जैसा आप उपयोग करते समय जीसीसी करते हैं __builtin_expect
निमो

6
@ नीमो, क्या आपने मेरे उत्तर के पहले वाक्य को पढ़ा है? आपने जो कुछ भी कहा है वह मेरे उत्तर द्वारा या दिए गए लिंक में शामिल है। यह सवाल पूछा गया कि क्या आप "हमेशा एक निश्चित तरीके से जाने के लिए शाखा भविष्यवाणी" कर सकते हैं, जिसका उत्तर "नहीं" है, और मुझे नहीं लगा कि अन्य उत्तर इस बारे में पर्याप्त स्पष्ट थे।
आर्टेलियस

4
ठीक है, मुझे और ध्यान से पढ़ना चाहिए था। ऐसा लगता है कि यह उत्तर तकनीकी रूप से सही है, लेकिन बेकार है, क्योंकि प्रश्नकर्ता स्पष्ट रूप से तलाश कर रहा है __builtin_expect। तो यह सिर्फ एक टिप्पणी होनी चाहिए। लेकिन यह असत्य नहीं है, इसलिए मैंने अपने पतन को हटा दिया है।
निमो

IMO यह बेकार नहीं है; यह एक उपयोगी स्पष्टीकरण है कि सीपीयू और कंपाइलर वास्तव में कैसे काम करते हैं, जो इन विकल्पों के साथ / बिना प्रदर्शन विश्लेषण के लिए प्रासंगिक हो सकता है। उदाहरण के लिए, आप आमतौर पर __builtin_expectएक परीक्षण-मामला बनाने के लिए उपयोग नहीं कर सकते हैं जिसे आप माप सकते हैं perf statकि एक बहुत ही उच्च शाखा का दुरुपयोग दर होगा। यह सिर्फ शाखा लेआउट को प्रभावित करता है । और बीटीडब्लू, इंटेल चूंकि सैंडब्रिज या कम से कम हैसवेल स्थिर भविष्यवाणी का उपयोग नहीं करता है / बिल्कुल भी नहीं; BHT में हमेशा कुछ भविष्यवाणी होती है, चाहे वह बासी उर्फ ​​हो या नहीं। xania.org/201602/bpu-part-two
पीटर कॉर्ड्स

24

C ++ 11 में संभावित / असंभावित मैक्रोज़ को परिभाषित करने का सही तरीका निम्नलिखित है:

#define LIKELY(condition) __builtin_expect(static_cast<bool>(condition), 1)
#define UNLIKELY(condition) __builtin_expect(static_cast<bool>(condition), 0)

यह विधि इसके विपरीत सभी C ++ संस्करणों के साथ संगत है [[likely]], लेकिन गैर-मानक एक्सटेंशन पर निर्भर करती है __builtin_expect


जब इन मैक्रोज़ ने इस तरह परिभाषित किया:

#define LIKELY(condition) __builtin_expect(!!(condition), 1)

यह ifबयानों के अर्थ को बदल सकता है और कोड को तोड़ सकता है। निम्नलिखित कोड पर विचार करें:

#include <iostream>

struct A
{
    explicit operator bool() const { return true; }
    operator int() const { return 0; }
};

#define LIKELY(condition) __builtin_expect((condition), 1)

int main() {
    A a;
    if(a)
        std::cout << "if(a) is true\n";
    if(LIKELY(a))
        std::cout << "if(LIKELY(a)) is true\n";
    else
        std::cout << "if(LIKELY(a)) is false\n";
}

और इसका आउटपुट:

if(a) is true
if(LIKELY(a)) is false

जैसा कि आप देख सकते हैं, LIKELY की परिभाषा एक शब्द के !!रूप में का उपयोग कर boolके शब्दार्थ को तोड़ने के लिए if

यहाँ बात यह नहीं है operator int()और operator bool()इसका संबंध होना चाहिए। जो अच्छा अभ्यास है।

बल्कि यह कि C ++ 11 प्रासंगिक रूपांतरणों के लिए संदर्भ खो देने के !!(x)बजाय उपयोग करना ।static_cast<bool>(x)


नोट सन्दर्भ रूपांतरण 2012 में एक दोष के माध्यम से आया था और 2014 के अंत में अभी भी कार्यान्वयन विचलन था। वास्तव में ऐसा लग रहा है कि मैं अभी भी जिस मामले से जुड़ा हूं वह gcc के लिए काम नहीं करता है।
शफीक यघमौर

@ शफीक्यगहमौर एक दिलचस्प अवलोकन है जिसमें संदर्भ रूपांतरण में शामिल है switch, धन्यवाद। यहां शामिल संदर्भ रूपांतरण टाइप करने के लिए आंशिक है boolऔर वहां सूचीबद्ध पांच विशिष्ट संदर्भ हैं , जिनमें switchसंदर्भ शामिल नहीं है।
मैक्सिम इगोरुशिन

यह केवल C ++ को प्रभावित करता है, है ना? इसलिए मौजूदा सी परियोजनाओं को उपयोग करने के लिए जाने और बदलने का कोई कारण नहीं है (_Bool)(condition), क्योंकि सी में ऑपरेटर ओवरलोडिंग नहीं है।
पीटर कॉर्डेस

2
आपके उदाहरण में, आपने सिर्फ उपयोग किया है (condition), नहीं !!(condition)। दोनों को trueबदलने के बाद (जी ++ 7.1 के साथ परीक्षण किया गया है)। क्या आप एक उदाहरण का निर्माण कर सकते हैं जो वास्तव में उस समस्या को प्रदर्शित करता है जिसके बारे में आप बोल रहे हैं जब आप !!बूलियनाइज़ का उपयोग करते हैं?
पीटर कॉर्ड्स

3
जैसा कि पीटर कॉर्डेस ने कहा है, आप कहते हैं "जब ये मैक्रोज़ [हैं] इस तरह से परिभाषित किया गया है:" और फिर '!!' का उपयोग करते हुए एक मैक्रो दिखाएं, "यदि कथन का अर्थ बदल जाए और कोड को तोड़ दें तो निम्नलिखित कोड पर विचार करें:" ... और फिर आप कोड दिखाते हैं जो '!!' का उपयोग नहीं करता है बिलकुल - जो C ++ 11 से पहले ही टूट जाना जानता है। कृपया एक उदाहरण दिखाने के लिए उत्तर को बदल दें जहाँ दिए गए मैक्रो (!! !!) का उपयोग गलत है।
कार्लो वुड

18

जैसा कि अन्य उत्तरों में सभी पर्याप्त रूप से सुझाव दिए गए हैं, आप __builtin_expectकंपाइलर को असेंबली कोड की व्यवस्था करने के बारे में संकेत देने के लिए उपयोग कर सकते हैं । जैसा कि आधिकारिक डॉक्स बताते हैं, ज्यादातर मामलों में, आपके मस्तिष्क में निर्मित कोडांतरक उतना अच्छा नहीं होगा जितना कि जीसीसी टीम द्वारा तैयार किया गया। अनुमान लगाने के बजाय अपने कोड को अनुकूलित करने के लिए वास्तविक प्रोफ़ाइल डेटा का उपयोग करना हमेशा सबसे अच्छा होता है।

समान पंक्तियों के साथ, लेकिन अभी तक उल्लेख नहीं किया गया है, एक जीसीसी-विशिष्ट तरीका है जो कंपाइलर को "ठंड" पथ पर कोड उत्पन्न करने के लिए मजबूर करता है। इसमें noinlineऔर coldविशेषताओं का उपयोग शामिल है , जो ठीक उसी तरह करते हैं जैसे वे करते हैं। इन विशेषताओं को केवल फ़ंक्शन के लिए लागू किया जा सकता है, लेकिन C ++ 11 के साथ, आप इनलाइन लैम्ब्डा फ़ंक्शन घोषित कर सकते हैं और इन दो विशेषताओं को लैम्ब्डा फ़ंक्शन पर भी लागू किया जा सकता है।

यद्यपि यह अभी भी एक माइक्रो-ऑप्टिमाइज़ेशन की सामान्य श्रेणी में आता है, और इस प्रकार मानक सलाह लागू होती है - परीक्षण का अनुमान नहीं है - मुझे लगता है कि यह आम तौर पर की तुलना में अधिक उपयोगी है __builtin_expect। शायद ही कोई x86 प्रोसेसर की कोई भी पीढ़ी शाखा भविष्यवाणी संकेत ( संदर्भ ) का उपयोग करती है, इसलिए आप जिस चीज को भी प्रभावित करने में सक्षम होने जा रहे हैं वह विधानसभा कोड का आदेश है। चूँकि आप जानते हैं कि एरर-हैंडलिंग या "एज केस" कोड क्या है, आप इस एनोटेशन का उपयोग यह सुनिश्चित करने के लिए कर सकते हैं कि कंपाइलर कभी भी इसकी शाखा की भविष्यवाणी नहीं करेगा और आकार के लिए अनुकूलन करने पर इसे "हॉट" कोड से दूर कर देगा।

नमूना उपयोग:

void FooTheBar(void* pFoo)
{
    if (pFoo == nullptr)
    {
        // Oh no! A null pointer is an error, but maybe this is a public-facing
        // function, so we have to be prepared for anything. Yet, we don't want
        // the error-handling code to fill up the instruction cache, so we will
        // force it out-of-line and onto a "cold" path.
        [&]() __attribute__((noinline,cold)) {
            HandleError(...);
        }();
    }

    // Do normal stuff
    
}

इससे भी बेहतर, जीसीसी स्वचालित रूप से उपलब्ध होने पर प्रोफ़ाइल प्रतिक्रिया के पक्ष में इसे अनदेखा कर देगा (उदाहरण के लिए, जब संकलन -fprofile-use)।

यहां आधिकारिक दस्तावेज देखें: https://gcc.gnu.org/oniltocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes


2
शाखा भविष्यवाणी संकेत उपसर्गों की अनदेखी की जाती है क्योंकि उनकी आवश्यकता नहीं होती है; आप अपने कोड को पुन: व्यवस्थित करके ठीक उसी प्रभाव को प्राप्त कर सकते हैं। (डिफ़ॉल्ट शाखा भविष्यवाणी एल्गोरिदम यह अनुमान लगाने के लिए है कि पिछड़ी शाखाएं ली गई हैं और आगे शाखाएं नहीं हैं।) इसलिए, आप वास्तव में सीपीयू को संकेत दे सकते हैं, और यह वही __builtin_expectकरता है। यह बिल्कुल भी बेकार नहीं है। आप सही हैं कि coldविशेषता भी उपयोगी है, लेकिन आप __builtin_expectमेरे विचार से उपयोगिता को कम आंकते हैं ।
निमो

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

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

क्या आपके पास "आधुनिक इंटेल सीपीयू स्थिर शाखा भविष्यवाणी का उपयोग नहीं करते हैं" का संदर्भ है? इंटेल का अपना लेख ( software.intel.com/en-us/articles/… ) अन्यथा कहता है ... लेकिन वह 2011 से है
निमो

वास्तव में एक आधिकारिक संदर्भ नहीं है, @ नीमो। इंटेल अपने चिप्स में उपयोग की जाने वाली शाखा भविष्यवाणी एल्गोरिदम के बारे में बेहद तंग है, उन्हें व्यापार रहस्य के रूप में मानते हैं। जो सबसे अधिक जाना जाता है उसे अनुभवजन्य परीक्षण द्वारा पता लगाया गया है। हमेशा की तरह, एग्नर फॉग की सामग्री सबसे अच्छे संसाधन हैं, लेकिन यहां तक ​​कि वे कहते हैं: "शाखा भविष्यवक्ता को हसवेल में फिर से डिज़ाइन किया गया प्रतीत होता है, लेकिन इसके निर्माण के बारे में बहुत कम जाना जाता है।" मुझे याद नहीं आ रहा है कि मैंने पहली बार कहाँ स्थैतिक बीपी का प्रदर्शन करते हुए बेंचमार्क देखा था, दुर्भाग्य से इसका उपयोग नहीं किया गया।
कोड़ी ग्रे

5

__builtin_expect का उपयोग संकलक को बताने के लिए किया जा सकता है कि आप किस शाखा से जाने की उम्मीद करते हैं। यह प्रभावित कर सकता है कि कोड कैसे उत्पन्न होता है। विशिष्ट प्रोसेसर तेजी से क्रमिक रूप से कोड चलाते हैं। तो अगर आप लिखेंगे

if (__builtin_expect (x == 0, 0)) ++count;
if (__builtin_expect (y == 0, 0)) ++count;
if (__builtin_expect (z == 0, 0)) ++count;

संकलक जैसे कोड उत्पन्न करेगा

if (x == 0) goto if1;
back1: if (y == 0) goto if2;
back2: if (z == 0) goto if3;
back3: ;
...
if1: ++count; goto back1;
if2: ++count; goto back2;
if3: ++count; goto back3;

यदि आपका संकेत सही है, तो यह बिना किसी शाखा के कोड निष्पादित करेगा। यह सामान्य अनुक्रम की तुलना में तेज़ी से चलेगा, जहां प्रत्येक अगर सशर्त कोड के आसपास शाखा करेगा और तीन शाखाओं को निष्पादित करेगा।

नए x86 प्रोसेसर में शाखाओं के लिए निर्देश हैं, जिन्हें लेने की अपेक्षा की जाती है, या उन शाखाओं के लिए जो अपेक्षित नहीं हैं (वहाँ एक अनुदेश उपसर्ग है, विवरण के बारे में निश्चित नहीं)। सुनिश्चित नहीं है कि अगर प्रोसेसर इसका उपयोग करता है। यह बहुत उपयोगी नहीं है, क्योंकि शाखा की भविष्यवाणी इसे ठीक से संभाल लेगी। इसलिए मुझे नहीं लगता कि आप वास्तव में शाखा की भविष्यवाणी को प्रभावित कर सकते हैं ।


2

ओपी के संबंध में, नहीं, जीसीसी के पास ऐसा कोई तरीका नहीं है जो प्रोसेसर को हमेशा यह मान सके कि शाखा है या नहीं। आपके पास __builtin_expect है, जो वह कहता है जो अन्य करता है। इसके अलावा, मुझे लगता है कि आप प्रोसेसर को यह नहीं बताना चाहते हैं कि क्या शाखा हमेशा ली जाती है या नहीं । आज के प्रोसेसर, जैसे कि इंटेल आर्किटेक्चर काफी जटिल पैटर्न को पहचान सकते हैं और प्रभावी ढंग से अनुकूलित कर सकते हैं।

हालांकि, कई बार आप यह नियंत्रित करना चाहते हैं कि क्या किसी शाखा द्वारा डिफ़ॉल्ट रूप से भविष्यवाणी की गई है या नहीं: जब आप जानते हैं कि कोड को ब्रांचिंग आंकड़ों के संबंध में "ठंडा" कहा जाएगा।

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.