2016-05-25 7 views
14
#include <stdio.h> 
int main(void) { 
    int i; 
    scanf("%d", &i); 
    if(i != 30) { return(0); } 
    printf("i is equal to %d\n", i); 
} 

ऐसा लगता है कि जिसके परिणामस्वरूप स्ट्रिंग हमेशा हो जाएगा "मैं 30 के बराबर है", हां, तो क्यों इस कॉल puts() के लिए एक कॉल के साथ printf के लिए अनुकूलन जीसीसी नहीं है, या write(), उदाहरण के लिए?जीसीसी इस कॉल को printf पर अनुकूलित क्यों नहीं करता है?

(बस उत्पन्न विधानसभा की जाँच की, gcc -O3 (संस्करण 5.3.1) के साथ, या Godbolt Compiler Explorer पर)

+1

'gcc'' printf' आउटपुट की भविष्यवाणी नहीं कर सकता (नहीं ..?)। – LPs

+1

@ एलपी AFAIK, यह printf() को कॉल() और putchar() जहां कॉल संभव हो, कॉल के साथ कॉल बदलता है। – Mitsos101

+3

@ मित्सोस 101 केवल * ज्ञात संकलन-समय स्थिरांक * के साथ। जो आप अपने कोड में देख रहे हैं उसे केवल इसे चलाकर निर्धारित किया जा सकता है। आपके सिर में करने के लिए आसान - एक कंपाइलर के लिए कम आसान है। – adelphus

उत्तर

11

सबसे पहले, समस्या if नहीं है; जैसा कि आपने देखा, gccif के माध्यम से देखता है और 30 को सीधे printf पास करने का प्रबंधन करता है।

अब, gcc कुछ तर्क printf के विशेष मामलों को संभालने के लिए है (विशेष रूप से, यह printf("something\n") और यहां तक ​​कि printf("%s\n", "something")puts("something") करने का अनुकूलन करता है), लेकिन यह अत्यंत विशिष्ट है और बहुत आगे जाना नहीं करता है; printf("Hello %s\n", "world"), उदाहरण के लिए, के रूप में छोड़ दिया गया है। इससे भी बदतर, से ऊपर के किसी भी प्रकार के के बिना किसी भी पिछली नई रेखा को छूटा नहीं जाता है, भले ही उन्हें fputs("something", stdout) में परिवर्तित किया जा सके।

मैं कल्पना है कि इस दो मुख्य समस्याओं के लिए नीचे आता है:

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

    यदि आप मुझसे पूछते हैं, तो ऊपर दिए गए puts ऑप्टिमाइज़ेशन पहले से ही "स्टाइल पॉइंट्स के लिए जा रहे हैं", आप वास्तव में कुछ भी कृत्रिम परीक्षण मामलों में गंभीर प्रदर्शन नहीं कर पाएंगे।

  • जब आप %s\n के दायरे से बाहर जाना शुरू करते हैं, printf एक minefield है, क्योंकि यह रनटाइम पर्यावरण पर मजबूत निर्भरता है; विशेष रूप से, कई printf विनिर्देशक (दुर्भाग्य से) लोकेल से प्रभावित होते हैं, साथ ही कार्यान्वयन-विशिष्ट क्विर्क और विनिर्देशकों का एक उछाल होता है (और gcc glibc, musl, mingw/msvcrt, से printf के साथ काम कर सकता है ...- और संकलन समय पर आप लक्ष्य सी रनटाइम का आह्वान नहीं कर सकते - सोचें कि जब आप क्रॉस-कंपाइलिंग करते हैं)।

    मैं मानता हूं कि यह आसान %d केस शायद सुरक्षित है, लेकिन मैं देख सकता हूं कि उन्होंने शायद अधिकतर स्मार्ट होने से बचने का फैसला क्यों किया और केवल यहां सबसे कमजोर और सुरक्षित अनुकूलन निष्पादित किए।


उत्सुक पाठक के लिए, here जहां इस अनुकूलन वास्तव में कार्यान्वित किया जाता है है; जैसा कि आप देख सकते हैं, फ़ंक्शन बहुत ही सरल मामलों की सीमित संख्या से मेल खाता है (और एक तरफ गिंपल, this nice article के बाद से बहुत कुछ नहीं बदला है)। संयोग से, स्रोत वास्तव में बताता है कि वे गैर-न्यूलाइन मामले के लिए fputs संस्करण को कार्यान्वित क्यों नहीं कर सके (उस संकलन चरण में stdout वैश्विक संदर्भित करने का कोई आसान तरीका नहीं है)।

+1

मैंने इस टिप्पणी को समाप्त करने से पहले अपना उदाहरण तय किया (जो एक नई लाइन के साथ समाप्त नहीं हुआ), लेकिन मैं इसे [godbolt लिंक वैसे भी पोस्ट करूंगा] (https://godbolt.org/g/mGzqZQ), जहां मैंने v3 जोड़ा और v4 एक उदाहरण के लिए मैंने पहले से ही कुछ और किया है। –

6

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

वैसे भी, आप इनपुट के बिना सभी कार्यक्रमों की अपेक्षा नहीं करते हैं puts() कथनों के लिए अनुकूलित किए जाने के बिना, इसलिए जीसीसी के लिए यह एक उचित है कि इस कोड को scanf() कथन युक्त अनुकूलित न करें।


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

+1

संकलक वास्तव में मुझे 30 के साथ बदल दिया, इसलिए यह इनपुट के बारे में परवाह नहीं करता है। यह जानने के लिए पर्याप्त चालाक नहीं है कि यह printf wiht – pm100

+0

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

1

यह सुनिश्चित नहीं है कि यह एक ठोस जवाब है, लेकिन मुझे उम्मीद है कि कंपेलरों को printf("%d\n", 10) केस puts("10") पर अनुकूलित नहीं करना चाहिए।

क्यों? क्योंकि यह मामला आपके विचार से ज्यादा जटिल हो सकता है। यहाँ समस्याओं मैं इस समय के बारे में सोच सकते हैं में से कुछ हैं:

  1. स्ट्रिंग शाब्दिक, और इस तरह कुल मिलाकर कोड आकार की ASCII बढ़ जाती है आकार में कनवर्ट द्विआधारी संख्या। हालांकि यह छोटी संख्याओं के लिए अप्रासंगिक है, लेकिन यदि यह printf("some number: %d", 10000) ---- 5 अंक या अधिक है (मानते हैं कि int 32-बिट है), स्ट्रिंग आकार में वृद्धि पूर्णांक के लिए सहेजे गए आकार को हरा देगी, और कुछ लोग इसे एक दोष । हां, रूपांतरण के साथ मैंने "स्टैक टू स्टैक" निर्देश सहेजा है, लेकिन निर्देश कितने बाइट्स हैं और कितने बचाए जाएंगे आर्किटेक्चर-विशिष्ट है। एक कंपाइलर के लिए यह कहना गैर-तुच्छ है कि यह इसके लायक है या नहीं।

  2. पैडिंग, यदि प्रारूपों में उपयोग किया जाता है, तो विस्तारित स्ट्रिंग का आकार भी बढ़ाया जा सकता है। उदाहरण: printf("some number: %10d", 100)

  3. कभी कभी मैं डेवलपर कोड आकार कारणों के लिए, printf कॉल के बीच एक प्रारूप स्ट्रिंग का हिस्सा होगा:

    printf("%-8s: %4d\n", "foo", 100); 
    printf("%-8s: %4d\n", "bar", 500); 
    printf("%-8s: %4d\n", "baz", 1000); 
    printf("%-8s: %4d\n", "something", 10000); 
    

    उन्हें अलग स्ट्रिंग शाब्दिक में कनवर्ट कर रहा आकार लाभ खो सकता है।

  4. %f, %e, और %g, दशमलव समस्या "।" लोकेल-निर्भर है। इसलिए संकलक इसे आपके लिए स्थिर स्ट्रिंग में विस्तारित नहीं कर सकता है। हालांकि हम केवल %d पर चर्चा कर रहे हैं, मैं यहां पूर्णता के लिए इसका उल्लेख करता हूं।

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