2017-05-18 11 views
20

मैं सभी मेरे कोड पर इस्तेमाल किया उस मैक्रो डिबग मोड में करता है:रनटाइम लागत के बिना दावाों के आधार पर जीसीसी अनुकूलन को कैसे मार्गदर्शित करें?

#define contract(condition) \ 
    if (!(condition)) \ 
     throw exception("a contract has been violated"); 

... लेकिन रिलीज़ मोड में:

#define contract(condition) \ 
    if (!(condition)) \ 
     __builtin_unreachable(); 

यदि ऐसा assert() से अधिक करता है कि है, रिलीज में बनाता है, कंपाइलर यूबी प्रचार के लिए धन्यवाद कोड को अनुकूलित कर सकता है।

उदाहरण के लिए

, निम्नलिखित कोड के साथ परीक्षण:

int foo(int i) { 
    contract(i == 1); 
    return i; 
} 

// ... 

foo(0); 

... डिबग मोड में एक अपवाद फेंकता है, लेकिन रिलीज़ मोड में बिना शर्त return 1; के लिए विधानसभा का उत्पादन:

foo(int): 
     mov  eax, 1 
     ret 

हालत , और उस पर निर्भर सब कुछ, अनुकूलित किया गया है।

मेरी समस्या अधिक जटिल स्थितियों के साथ उत्पन्न होती है। जब संकलक साबित नहीं कर सकता है कि स्थिति का कोई दुष्प्रभाव नहीं है, तो यह इसे अनुकूलित नहीं करता है, जो अनुबंध का उपयोग न करने की तुलना में रनटेम जुर्माना है।

क्या यह व्यक्त करने का कोई तरीका है कि अनुबंध की स्थिति का कोई दुष्प्रभाव नहीं है, ताकि हमेशा अनुकूलित हो?

+0

टिप्पणियाँ विस्तृत चर्चा के लिए नहीं हैं, यह वार्तालाप [चैट करने के लिए स्थानांतरित हो गया है] (http://chat.stackoverflow.com/rooms/144609/discussion-on-question-by-lyingonthesky-how-to-guide-gcc-optimizations-based-on)। – deceze

उत्तर

2

कोड को ऑप्टिमाइज़ करने के लिए कोई रास्ता नहीं है-अगर यह मृत कोड था, क्योंकि जीसीसी को हमेशा मानक के साथ शिकायत करना है।

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

एक मैक्रो का एक उदाहरण है जो कुछ भी बाहर अनुकूलित है जाँच करता है और यूबी प्रचार करता है कि:

#define _contract(condition) \ 
    { 
     ([&]() __attribute__ ((noinline,error ("contract could not be optimized out"))) { 
      if (condition) {} // using the condition in if seem to hide `unused` warnings. 
     }()); 
     if (!(condition)) 
      __builtin_unreachable(); 
    } 

त्रुटि विशेषता अनुकूलन के बिना काम नहीं करता है (ताकि इस मैक्रो केवल रिहाई \ अनुकूलन मोड संकलन के लिए इस्तेमाल किया जा सकता है) । ध्यान दें कि त्रुटि जो दर्शाती है कि अनुबंध के साइड इफेक्ट्स के संबंध में जो कुछ भी है, वह जुड़ाव के दौरान दिखाया गया है।

A test that shows an error with unoptimizable contract.

A test that optimizes out a contract but, does UB propagation with it.

5

तो, कोई जवाब नहीं, लेकिन कुछ दिलचस्प परिणाम जो कहीं भी नेतृत्व कर सकते हैं।

मैं निम्नलिखित खिलौना कोड के साथ समाप्त हो गया:

#define contract(x) \ 
    if (![&]() __attribute__((pure, noinline)) { return (x); }()) \ 
     __builtin_unreachable(); 

bool noSideEffect(int i); 

int foo(int i) { 
    contract(noSideEffect(i)); 

    contract(i == 1); 

    return i; 
} 

आप कर सकते हैं follow along at home भी;)

noSideEffect समारोह जो हम जानते हैं कोई साइड इफेक्ट नहीं है, लेकिन संकलक नहीं करता है।
यह इस तरह से चला गया:

  1. जीसीसी __attribute__((pure)) है कोई पक्ष प्रभाव होने के रूप में एक समारोह में चिन्हित करें।

  2. noSideEffect योग्यता pure विशेषता के साथ फ़ंक्शन कॉल को पूरी तरह हटा देता है। अच्छा!

  3. लेकिन हम noSideEffect की घोषणा को संशोधित नहीं कर सकते हैं। तो अपने कॉल को एक ऐसे फ़ंक्शन के अंदर लपेटने के बारे में जो pure है? और चूंकि हम एक आत्मनिर्भर मैक्रो बनाने की कोशिश कर रहे हैं, एक भेड़ का बच्चा अच्छा लगता है।

  4. आश्चर्य की बात है, यह काम नहीं करता है ... जब तक हम लैम्ब्डा में noinline जोड़ते हैं! मुझे लगता है कि noSideEffect पर कॉल को ऑप्टिमाइज़ करने पर विचार करने से पहले ऑप्टिमाइज़र पहले लैम्ब्डा को रेखांकित करता है, pure विशेषता को खो देता है। noinline के साथ, कुछ हद तक प्रतिद्वंद्वी तरीके से, अनुकूलक सब कुछ मिटा देता है। महान!

  5. हालांकि, अब दो मुद्दे हैं: noinline विशेषता के साथ, कंपाइलर प्रत्येक लैम्ब्डा के लिए एक शरीर उत्पन्न करता है, भले ही उनका उपयोग कभी न किया जाए। मेह - लिंकर शायद उन्हें किसी भी तरह से फेंकने में सक्षम होगा।
    लेकिन अधिक महत्वपूर्ण बात ... हम वास्तव में अनुकूलन नहीं है जो __builtin_unreachable() सक्षम था :(

यह कुल मिलाकर, खो दिया है, जिसे आप निकालना या इसके बाद के स्निपेट में वापस noinline रख सकते हैं, और इनमें से किसी एक के साथ खत्म परिणाम:

साथ noinline

; Unused code 
foo(int)::{lambda()#2}::operator()() const: 
     mov  rax, QWORD PTR [rdi] 
     cmp  DWORD PTR [rax], 1 
     sete al 
     ret 
foo(int)::{lambda()#1}::operator()() const: 
     mov  rax, QWORD PTR [rdi] 
     mov  edi, DWORD PTR [rax] 
     jmp  noSideEffect(int) 

; No function call, but the access to i is performed 
foo(int): 
     mov  eax, edi 
     ret 

बिना

; No unused code 
; Access to i has been optimized out, 
; but the call to `noSideEffect` has been kept. 
foo(int): 
     sub  rsp, 8 
     call noSideEffect(int) 
     mov  eax, 1 
     add  rsp, 8 
     ret 
+1

मैंने इस खिलौने के साथ काफी कुछ खेला, मुझे लगता है कि यह जीसीसी में एक बग है कि यह 'शुद्ध' विशेषता को स्ट्रिप करता है जब यह इसे रेखांकित करता है, वैसे भी ज्ञान के लिए धन्यवाद। – LyingOnTheSky

4

वहाँ है कि अनुबंध में शर्त कोई साइड प्रभाव पड़ता है व्यक्त करने के लिए एक रास्ता है, इतना है कि यह हमेशा बाहर अनुकूलित है?

संभावना नहीं है।

यह ज्ञात है कि आप धारणाओं का एक बड़ा संग्रह नहीं ले सकते हैं, उन्हें धारणाओं में बदल सकते हैं (__builtin_unreachable के माध्यम से) और अच्छे परिणाम (उदा। Assertions Are Pessimistic, Assumptions Are Optimistic जॉन रेगेर द्वारा) की उम्मीद करें।

कुछ सुराग:

  • बजना, जबकि पहले से ही __builtin_unreachable आंतरिक होने, __builtin_assume वास्तव में इस उद्देश्य के लिए पेश किया।

  • N4425 - Generalized Dynamic Assumptions(*) नोटों कि:

    जीसीसी स्पष्ट रूप से एक आम धारणा सुविधा प्रदान नहीं करता है, लेकिन सामान्य मान्यताओं __builtin_unreachable आंतरिक

    नियंत्रण प्रवाह का एक संयोजन और का उपयोग कर एन्कोड किया जा सकता ...

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

  • दिशानिर्देश समर्थन लाइब्रेरी (GSL, माइक्रोसॉफ्ट द्वारा की मेजबानी की है, लेकिन कोई रास्ता नहीं में माइक्रोसॉफ्ट विशिष्ट) "केवल" इस कोड है:

    #ifdef _MSC_VER 
    #define GSL_ASSUME(cond) __assume(cond) 
    #elif defined(__clang__) 
    #define GSL_ASSUME(cond) __builtin_assume(cond) 
    #elif defined(__GNUC__) 
    #define GSL_ASSUME(cond) ((cond) ? static_cast<void>(0) : __builtin_unreachable()) 
    #else 
    #define GSL_ASSUME(cond) static_cast<void>(!!(cond)) 
    #endif 
    

    और लिखते हैं कि:

    // GSL_ASSUME(cond) 
    // 
    // Tell the optimizer that the predicate cond must hold. It is unspecified 
    // whether or not cond is actually evaluated. 
    

*) पेपर rejected: ईडब्ल्यूजी का मार्गदर्शन प्रस्तावित अनुबंध सुविधाओं के भीतर कार्यक्षमता प्रदान करना था।

+0

क्या मैं जान सकता हूं कि "cond" क्या करता है और कहां घोषित किया जाता है? –

+1

@ jack_1729 'cond' फ़ंक्शन-जैसी मैक्रो का इनपुट पैरामीटर है (https://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments.html पर एक नज़र डालें)। 'जीएसएल_एएसएसयूएमई' एक बुलियन अभिव्यक्ति/शर्त ('cond') की अपेक्षा करता है। – manlio

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