कर्नेल में संभावना और असंभावना कॉल के बीच अंतर क्या है?


11

कर्नेल में संभावित और असंभावित कॉल के बीच क्या है। कर्नेल स्रोत के माध्यम से खोज करते समय मुझे ये कथन मिले।

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

क्या कोई इसमें कुछ प्रकाश डाल सकता है?


यह वास्तव में एक प्रोग्रामिंग सवाल है, जो स्टैक ओवेरफ्लो के लिए बेहतर अनुकूल है ।
गिलेस एसओ- बुराई को रोकना '

जवाबों:


14

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

उनका उपयोग इस तरह किया जाता है:

if (likely(some_condition)) {
  // the compiler will try and make the code layout optimal for the case
  // where some_condition is true, i.e. where this block is run
  most_likely_action();
} else {
  // this block is less frequently used
  corner_case();
}

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

कोड को कैसे अनुकूलित किया जा सकता है इसके कुछ उदाहरणों को आसानी से खोजकर पाया जाता है GCC __builtin_expect। यह ब्लॉग पोस्ट gcc ऑप्टिमाइज़ेशन: __builtin_expect उदाहरण के लिए इसके साथ एक डिस्सैड का विवरण देता है।

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

इसलिए संकलक कोड को ऐसे उत्सर्जित करेगा कि सबसे अधिक संभावना वाली शाखा में जंप शामिल नहीं होगा यदि वह लक्ष्य CPU पसंद करता है, उदाहरण के लिए।


यूनिकॉर्न का क्या मतलब है ? क्या यह एक तकनीकी शब्द है या सिर्फ एक भराव है?
सेन

मैंने भ्रम से बचने के लिए इकसिंगों को हटा दिया।
Mat

क्या आप कृपया संकलक के बारे में विस्तार से बताने की कोशिश करेंगे और केस के लिए कोड लेआउट को इष्टतम बनाएंगे ? मैं जानना चाहूंगा कि यह कैसे होता है।
सेन

उस पर थोड़ी जानकारी जोड़ी। कोड के अनुकूलन का कोई सामान्य तरीका नहीं है, यह सब बहुत प्रोसेसर पर निर्भर है।
Mat

2

जीसीसी 4.8 इसके साथ क्या करता है यह देखने के लिए विघटित करते हैं

बिना उम्मीद के

#include "stdio.h"
#include "time.h"

int main() {
    /* Use time to prevent it from being optimized away. */
    int i = !time(NULL);
    if (i)
        printf("%d\n", i);
    puts("a");
    return 0;
}

संकलन और जीसीसी 4.8.2 x86_64 लिनक्स के साथ विघटित:

gcc -c -O3 -std=gnu11 main.c
objdump -dr main.o

आउटपुट:

0000000000000000 <main>:
   0:       48 83 ec 08             sub    $0x8,%rsp
   4:       31 ff                   xor    %edi,%edi
   6:       e8 00 00 00 00          callq  b <main+0xb>
                    7: R_X86_64_PC32        time-0x4
   b:       48 85 c0                test   %rax,%rax
   e:       75 14                   jne    24 <main+0x24>
  10:       ba 01 00 00 00          mov    $0x1,%edx
  15:       be 00 00 00 00          mov    $0x0,%esi
                    16: R_X86_64_32 .rodata.str1.1
  1a:       bf 01 00 00 00          mov    $0x1,%edi
  1f:       e8 00 00 00 00          callq  24 <main+0x24>
                    20: R_X86_64_PC32       __printf_chk-0x4
  24:       bf 00 00 00 00          mov    $0x0,%edi
                    25: R_X86_64_32 .rodata.str1.1+0x4
  29:       e8 00 00 00 00          callq  2e <main+0x2e>
                    2a: R_X86_64_PC32       puts-0x4
  2e:       31 c0                   xor    %eax,%eax
  30:       48 83 c4 08             add    $0x8,%rsp
  34:       c3                      retq

स्मृति में निर्देश क्रम अपरिवर्तित था: पहले printfऔर फिर putsऔर फिर retqवापसी।

उम्मीद के साथ

अब इसके if (i)साथ बदलें :

if (__builtin_expect(i, 0))

और हमें मिलता है:

0000000000000000 <main>:
   0:       48 83 ec 08             sub    $0x8,%rsp
   4:       31 ff                   xor    %edi,%edi
   6:       e8 00 00 00 00          callq  b <main+0xb>
                    7: R_X86_64_PC32        time-0x4
   b:       48 85 c0                test   %rax,%rax
   e:       74 11                   je     21 <main+0x21>
  10:       bf 00 00 00 00          mov    $0x0,%edi
                    11: R_X86_64_32 .rodata.str1.1+0x4
  15:       e8 00 00 00 00          callq  1a <main+0x1a>
                    16: R_X86_64_PC32       puts-0x4
  1a:       31 c0                   xor    %eax,%eax
  1c:       48 83 c4 08             add    $0x8,%rsp
  20:       c3                      retq
  21:       ba 01 00 00 00          mov    $0x1,%edx
  26:       be 00 00 00 00          mov    $0x0,%esi
                    27: R_X86_64_32 .rodata.str1.1
  2b:       bf 01 00 00 00          mov    $0x1,%edi
  30:       e8 00 00 00 00          callq  35 <main+0x35>
                    31: R_X86_64_PC32       __printf_chk-0x4
  35:       eb d9                   jmp    10 <main+0x10>

printf(संकलित __printf_chk), समारोह के अंत करने के लिए ले जाया गया था के बाद putsके रूप में अन्य उत्तर ने उल्लेख किया और बदले शाखा भविष्यवाणी में सुधार होगा।

तो यह मूल रूप से एक ही है:

int i = !time(NULL);
if (i)
    goto printf;
puts:
puts("a");
return 0;
printf:
printf("%d\n", i);
goto puts;

यह अनुकूलन के साथ नहीं किया गया था -O0

लेकिन __builtin_expectबिना किसी उदाहरण के तेजी से चलने वाला उदाहरण लिखने का सौभाग्य , उन दिनों सीपीयू वास्तव में स्मार्ट हैं । मेरी भोली कोशिश यहाँ हैं

सी ++ 20 [[likely]]और[[unlikely]]

C ++ 20 ने उन C ++ बिल्ट-इन को मानकीकृत किया है: /programming/51797959/how-to-use-c20s-likely-unlikely-attribute-in-if-else-statement वे संभवतः (ए) दंड!) एक ही काम करते हैं।

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