मृत कोड का पता लगाने के लिए संकलक द्वारा पूरी तरह से हल क्यों नहीं किया जा सकता है?


192

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


91
एक लूप बनाएं, उसके बाद कोड डालें, उसके बाद en.wikipedia.org/wiki/Halting_problem
zapl

48
if (isPrime(1234234234332232323423)){callSomething();}क्या यह कोड कभी कुछ कॉल करेगा या नहीं? ऐसे कई अन्य उदाहरण हैं, जहां कभी किसी समारोह को तय करना कार्यक्रम में इसे शामिल करने से कहीं अधिक महंगा होता है।
idclev 463035818

33
public static void main(String[] args) {int counterexample = findCollatzConjectureCounterexample(); System.out.println(counterexample);}<- प्रिंटल कॉल डेड कोड है? इंसान भी नहीं सुलझा सकता!
user253751

15
@ tobi303 एक महान उदाहरण नहीं है, यह वास्तव में फैक्टर प्राइम नंबरों के लिए आसान है ... बस उन्हें अपेक्षाकृत कुशलता से फैक्टर करने के लिए नहीं। हॉल्टिंग की समस्या एनपी में नहीं है, यह अकारण है।
en_Knight

57
@alephzero और en_Knight - आप दोनों गलत हैं। isPrime एक महान उदाहरण है। आपने एक अनुमान लगाया कि फ़ंक्शन एक प्रधान संख्या के लिए जाँच कर रहा है। हो सकता है कि वह नंबर एक सीरियल नंबर था और यह देखने के लिए एक डेटाबेस खोज करता है कि क्या उपयोगकर्ता एक अमेज़ॅन प्राइम सदस्य है? कारण यह एक महान उदाहरण है क्योंकि यह जानने का एकमात्र तरीका है कि क्या स्थिति स्थिर है या नहीं, वास्तव में isPrime फ़ंक्शन को निष्पादित करना है। इसलिए अब कंपाइलर को एक दुभाषिया होना चाहिए। लेकिन यह अभी भी उन मामलों को हल नहीं करेगा जहां डेटा अस्थिर है।
डंक

जवाबों:


275

मृत कोड समस्या हॉल्टिंग समस्या से संबंधित है

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

यह डेड कोड से कैसे संबंधित है?

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

हॉल्ट समस्या के लिए एक एल्गोरिथ्म में मृत कोड के लिए आप एक एल्गोरिथ्म कैसे स्थानांतरित करते हैं?

सरल: आप उस कार्यक्रम के अंत के बाद कोड की एक पंक्ति जोड़ते हैं जिसे आप पड़ाव के लिए जाँचना चाहते हैं। यदि आपका डेड-कोड डिटेक्टर यह पता लगाता है कि यह लाइन मृत है, तो आप जानते हैं कि प्रोग्राम रुक नहीं रहा है। यदि ऐसा नहीं होता है, तो आप जानते हैं कि आपका कार्यक्रम रुक जाता है (अंतिम पंक्ति में, और फिर आपके जोड़े गए कोड में)।


कंपाइलर आमतौर पर उन चीजों की जांच करते हैं, जिन्हें मृत होने के लिए संकलन-समय पर सिद्ध किया जा सकता है। उदाहरण के लिए, ब्लॉक जो उन परिस्थितियों पर निर्भर हैं जो संकलन के समय गलत होने के लिए निर्धारित किए जा सकते हैं। या return( एक ही दायरे के भीतर) के बाद कोई बयान ।

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


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

50
@Vality 64-बिट प्रोसेसर 2 ^ 64 बाइट्स को संबोधित कर सकते हैं। सभी 256 ^ (2 ^ 64) राज्यों को खोजने का मज़ा लें!
डैनियल वैगनर

82
@DanielWagner यह एक समस्या नहीं होनी चाहिए। खोज करने वाले 256^(2^64)राज्य हैं O(1), इसलिए बहुपद समय में मृत कोड का पता लगाया जा सकता है।
ऐनाबिस

13
@ लेलियल, वह व्यंग्य था।
पॉल ड्रेपर

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

77

ठीक है, चलो रुकने की समस्या की अनिश्चिता का शास्त्रीय प्रमाण लें और हॉल्ट-डिटेक्टर को एक मृत-कोड डिटेक्टर में बदल दें!

सी # कार्यक्रम

using System;
using YourVendor.Compiler;

class Program
{
    static void Main(string[] args)
    {
        string quine_text = @"using System;
using YourVendor.Compiler;

class Program
{{
    static void Main(string[] args)
    {{
        string quine_text = @{0}{1}{0};
        quine_text = string.Format(quine_text, (char)34, quine_text);

        if (YourVendor.Compiler.HasDeadCode(quine_text))
        {{
            System.Console.WriteLine({0}Dead code!{0});
        }}
    }}
}}";
        quine_text = string.Format(quine_text, (char)34, quine_text);

        if (YourVendor.Compiler.HasDeadCode(quine_text))
        {
            System.Console.WriteLine("Dead code!");
        }
    }
}

अगर YourVendor.Compiler.HasDeadCode(quine_text)रिटर्न false, तो लाइन System.Console.WriteLn("Dead code!");कभी, निष्पादित नहीं किया जाएगा ताकि इस कार्यक्रम वास्तव में है मृत कोड है, और डिटेक्टर गलत था।

लेकिन अगर यह वापस आ जाता है true, तो लाइन System.Console.WriteLn("Dead code!");निष्पादित की जाएगी, और चूंकि कार्यक्रम में अधिक कोड नहीं है, इसलिए कोई मृत कोड नहीं है, इसलिए फिर से, डिटेक्टर गलत था।

तो आपके पास यह है, एक मृत-कोड डिटेक्टर जो केवल "मृत कोड है" या "कोई मृत कोड नहीं है" कभी-कभी गलत जवाब देता है।


1
यदि मैंने आपके तर्क को सही ढंग से समझा है, तो तकनीकी रूप से एक और विकल्प यह होगा कि यह एक काफी लिखना संभव नहीं है जो एक मृत कोड डिटेक्टर है, लेकिन सामान्य मामले में एक मृत कोड डिटेक्टर लिखना संभव है। :-)
abligh

1
गोडेलियन जवाब के लिए वेतन वृद्धि।
जेरेड स्मिथ

@ बालि उघ, यह शब्दों का एक बुरा विकल्प था। मैं वास्तव में मृत-कोड डिटेक्टर के स्रोत कोड को स्वयं नहीं खिला रहा हूं, लेकिन प्रोग्राम का स्रोत कोड जो इसका उपयोग करता है। निश्चित रूप से, कुछ बिंदु पर शायद इसके अपने कोड को देखना होगा, लेकिन यह इसका व्यवसाय है।
जोकर_vD

65

यदि रुकने की समस्या बहुत अस्पष्ट है, तो इस तरह से सोचें।

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

 for (BigInt n = 4; ; n+=2) {
     if (!isGoldbachsConjectureTrueFor(n)) {
         print("Conjecture is false for at least one value of n\n");
         exit(0);
     }
 }

कार्यान्वयन को isGoldbachsConjectureTrueFor()पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है, लेकिन इस उद्देश्य के लिए कम से कम सभी अपराधों पर एक सरल पुनरावृत्ति हो सकती हैn

अब, तार्किक रूप से ऊपर या तो के बराबर होना चाहिए:

 for (; ;) {
 }

(यानी एक अनंत लूप) या

print("Conjecture is false for at least one value of n\n");

गोल्डबैक के अनुमान के रूप में या तो सच होना चाहिए या सच नहीं होना चाहिए। यदि कोई कंपाइलर हमेशा मृत कोड को समाप्त कर सकता है, तो निश्चित रूप से किसी भी स्थिति में यहां समाप्त करने के लिए मृत कोड होगा। हालाँकि, ऐसा करने में बहुत कम से कम आपके संकलक को मनमाने ढंग से कठिन समस्याओं को हल करने की आवश्यकता होगी। हम समस्याओं को बहुत मुश्किल से प्रदान कर सकते हैं कि इसे हल करना होगा (जैसे एनपी-पूर्ण समस्याएं) यह निर्धारित करने के लिए कि कोड के किस बिट को समाप्त करना है। उदाहरण के लिए यदि हम यह कार्यक्रम लेते हैं:

 String target = "f3c5ac5a63d50099f3b5147cabbbd81e89211513a92e3dcd2565d8c7d302ba9c";
 for (BigInt n = 0; n < 2**2048; n++) {
     String s = n.toString();
     if (sha256(s).equals(target)) {
         print("Found SHA value\n");
         exit(0);
     }
 }
 print("Not found SHA value\n");

हम जानते हैं कि कार्यक्रम या तो "मिला SHA मूल्य" या "नहीं मिला SHA मूल्य" प्रिंट करेगा (बोनस अंक यदि आप मुझे बता सकते हैं कि कौन सा सच है)। हालांकि, एक संकलक के लिए 2 ^ 2048 पुनरावृत्तियों के आदेश का यथोचित अनुकूलन करने में सक्षम होना चाहिए। यह वास्तव में एक महान अनुकूलन होगा क्योंकि जैसा कि मैंने उपरोक्त कार्यक्रम की भविष्यवाणी की है (या हो सकता है) तब तक चलेगा जब तक कि अनुकूलन के बिना कुछ भी छापने के बजाय ब्रह्मांड की गर्मी से मृत्यु न हो।


4
इसका सबसे अच्छा उत्तर अब तक +1
जीन

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

2
2 ^ 2048 पुनरावृत्तियों? यहां तक ​​कि डीप थॉट देना भी छोड़ देता।
पीटर मोर्टेंसन

यह बहुत उच्च संभावना के साथ "मिला SHA मूल्य" प्रिंट करेगा, भले ही वह लक्ष्य 64 हेक्स अंकों का यादृच्छिक स्ट्रिंग था। जब तक sha256एक बाइट सरणी और बाइट सरणियाँ वापस नहीं आती हैं, तब तक आपकी भाषा में तार के बराबर तुलना नहीं होती है।
user253751

4
Implementation of isGoldbachsConjectureTrueFor() is left as an exercise for the readerइसने मुझे चकित कर दिया।
biziclop

34

मुझे नहीं पता कि C ++ या Java में कोई Evalफ़ंक्शन है, लेकिन कई भाषाएं आपको नाम से कॉल करने की अनुमति देती हैं । निम्नलिखित (contrived) VBA उदाहरण पर विचार करें।

Dim methodName As String

If foo Then
    methodName = "Bar"
Else
    methodName = "Qux"
End If

Application.Run(methodName)

कहा जाने वाला विधि का नाम रनटाइम तक जानना असंभव है। इसलिए, परिभाषा के अनुसार, कंपाइलर पूरी निश्चितता के साथ नहीं जान सकता है कि किसी विशेष विधि को कभी नहीं कहा जाता है।

दरअसल, नाम से एक विधि को कॉल करने का उदाहरण देते हुए, ब्रांचिंग तर्क भी आवश्यक नहीं है। बस कह रहा है

Application.Run("Bar")

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


2
जावा (या C #) में, यह प्रतिबिंब के साथ किया जा सकता है। सी ++ आप शायद ऐसा करने के लिए मैक्रोज़ का उपयोग करके कुछ नाक बंद कर सकते हैं। सुंदर नहीं होगा, लेकिन सी ++ शायद ही कभी हो।
डारेल हॉफमैन

6
@DarrelHoffman - कोड संकलक को दिए जाने से पहले मैक्रोज़ का विस्तार किया जाता है, इसलिए मैक्रोज़ निश्चित रूप से नहीं हैं कि आप यह कैसे करेंगे। कार्यों के लिए संकेत है कि आप यह कैसे करेंगे। मैंने वर्षों में C ++ का उपयोग नहीं किया है, तो मुझे क्षमा करें यदि मेरे सटीक प्रकार के नाम गलत हैं, लेकिन आप केवल स्ट्रिंगर्स के नक्शे को फ़ंक्शन करने के लिए स्टोर कर सकते हैं। फिर कुछ ऐसा है जो उपयोगकर्ता इनपुट से एक स्ट्रिंग को स्वीकार करता है, नक्शे में उस स्ट्रिंग को देखता है, और फिर उस फ़ंक्शन को निष्पादित करता है जिसे इंगित किया गया है।
ArtOfWarfare

1
@ArtOfWarfare हम बात नहीं कर रहे हैं कि यह कैसे किया जा सकता है। जाहिर है, इस स्थिति को खोजने के लिए कोड का सिमेंटिक विश्लेषण किया जा सकता है, बिंदु यह था कि कंपाइलर नहीं है । यह, संभवतः, हो सकता है, लेकिन यह नहीं है।
रबरडाक

3
@ArtOfWarfare: यदि आप निपिक करना चाहते हैं, तो सुनिश्चित करें। मैं प्रीप्रोसेसर को कंपाइलर का हिस्सा मानता हूं, हालांकि मुझे पता है कि यह तकनीकी रूप से नहीं है। किसी भी तरह, फ़ंक्शन पॉइंटर्स नियम को तोड़ सकते हैं कि फ़ंक्शंस को सीधे कहीं भी संदर्भित नहीं किया जाता है - वे सीधे कॉल के बजाय एक सूचक के रूप में हैं, बहुत सी # में एक प्रतिनिधि की तरह। C ++ सामान्य तौर पर एक कंपाइलर के लिए पूर्वानुमान लगाने में अधिक कठिन है क्योंकि इसमें परोक्ष रूप से चीजों को करने के कई तरीके हैं। यहां तक ​​कि "सभी संदर्भों को ढूंढें" के रूप में सरल कार्य भी तुच्छ नहीं हैं, क्योंकि वे टाइपराइफ़, मैक्रोज़ आदि में छिपा सकते हैं। कोई आश्चर्य नहीं कि यह आसानी से मृत कोड नहीं पा सकता है।
डारेल हॉफमैन

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

12

बिना शर्त मृत कोड का पता लगाया जा सकता है और उन्नत संकलक द्वारा हटाया जा सकता है।

लेकिन सशर्त मृत कोड भी है। वह कोड है जिसे संकलन के समय नहीं जाना जा सकता है और केवल रनटाइम के दौरान पता लगाया जा सकता है। उदाहरण के लिए, एक सॉफ्टवेयर उपयोगकर्ता की पसंद के आधार पर कुछ विशेषताओं को शामिल करने या बाहर करने के लिए कॉन्फ़िगर करने योग्य हो सकता है, जिससे कोड के कुछ खंड विशेष परिदृश्य में मृत प्रतीत होते हैं। यह वास्तविक मृत कोड नहीं है।

ऐसे विशिष्ट उपकरण हैं जो परीक्षण कर सकते हैं, निर्भरता को हल कर सकते हैं, सशर्त मृत कोड को हटा सकते हैं और दक्षता के लिए रनटाइम पर उपयोगी कोड को फिर से जोड़ सकते हैं। इसे डायनामिक डेड कोड एलिमिनेशन कहा जाता है। लेकिन जैसा कि आप देख सकते हैं कि यह संकलक के दायरे से परे है।


5
"बिना शर्त मृत कोड का पता लगाया जा सकता है और उन्नत संकलक द्वारा हटाया जा सकता है।" इसकी संभावना नहीं लगती है। कोड की मर्यादा किसी दिए गए फ़ंक्शन के परिणाम पर निर्भर कर सकती है, और यह दिए गए फ़ंक्शन मनमानी समस्याओं को हल कर सकते हैं। इसलिए आपका कथन यह दावा करता है कि उन्नत संकलनकर्ता मनमानी समस्याओं को हल कर सकते हैं।
तैमूर

6
@Taemyr तो यह बिना शर्त मर जाना नहीं होगा, अब यह होगा?
JAB

1
@Taemyr आपको "बिना शर्त" शब्द गलत लगता है। यदि कोड डेडनेस किसी फ़ंक्शन के परिणाम पर निर्भर करता है, तो यह सशर्त डेड कोड है। "स्थिति" फ़ंक्शन का परिणाम है। "बिना शर्त" होने के लिए किसी भी परिणाम पर निर्भर नहीं होना होगा ।
काइओटिक

12

एक सरल उदाहरण:

int readValueFromPort(const unsigned int portNum);

int x = readValueFromPort(0x100); // just an example, nothing meaningful
if (x < 2)
{
    std::cout << "Hey! X < 2" << std::endl;
}
else
{
    std::cout << "X is too big!" << std::endl;
}

अब मान लें कि पोर्ट 0x100 को केवल 0 या 1 पर लौटने के लिए डिज़ाइन किया गया है। उस स्थिति में कंपाइलर यह पता नहीं लगा सकता है कि elseब्लॉक कभी निष्पादित नहीं होगा।

हालांकि इस मूल उदाहरण में:

bool boolVal = /*anything boolean*/;

if (boolVal)
{
  // Do A
}
else if (!boolVal)
{
  // Do B
}
else
{
  // Do C
}

यहां कंपाइलर गणना कर सकता है कि elseब्लॉक एक मृत कोड है। इसलिए कंपाइलर मृत कोड के बारे में केवल तभी चेतावनी दे सकता है, जब उसके पास मृत कोड का पता लगाने के लिए पर्याप्त डेटा हो और यह भी पता होना चाहिए कि दिए गए ब्लॉक का मृत कोड है या नहीं, यह पता लगाने के लिए उस डेटा को कैसे लागू किया जाए।

संपादित करें

कभी-कभी डेटा संकलन समय पर उपलब्ध नहीं होता है:

// File a.cpp
bool boolMethod();

bool boolVal = boolMethod();

if (boolVal)
{
  // Do A
}
else
{
  // Do B
}

//............
// File b.cpp
bool boolMethod()
{
    return true;
}

संकलन करते समय। संकलक यह नहीं जान सकता है कि boolMethodहमेशा रिटर्न true


1
हालांकि कड़ाई से सच है कि संकलक को पता नहीं है, मुझे लगता है कि यह सवाल पूछने की भावना में है कि क्या लिंकर को पता चल सकता है।
केसी कुबैल

1
@Darthfett यह लिंकर की जिम्मेदारी नहीं है। लिंकर संकलित कोड की सामग्री का विश्लेषण नहीं करता है। लिंकर (आम तौर पर बोलना) सिर्फ तरीकों और वैश्विक डेटा को जोड़ता है, यह सामग्री के बारे में परवाह नहीं करता है। हालाँकि कुछ कंपाइलरों के पास स्रोत फ़ाइलों (जैसे ICC) को समाप्‍त करने और फिर अनुकूलन करने का विकल्‍प होता है। ऐसे मामले में EDIT के तहत मामला कवर किया गया है, लेकिन यह विकल्प विशेष रूप से संकलन समय को प्रभावित करेगा जब परियोजना बड़ी है।
एलेक्स लोप।

यह उत्तर मुझे भ्रामक लगता है; आप दो उदाहरण दे रहे हैं जहां यह संभव नहीं है क्योंकि सभी जानकारी उपलब्ध नहीं है, लेकिन क्या आपको यह नहीं कहना चाहिए कि जानकारी होने पर भी यह असंभव है?
एंटोन गोलोव

@AntonGolovIt ओएस हमेशा सच नहीं है। कई मामलों में जब जानकारी होती है, तो कंपाइलर मृत कोड का पता लगा सकते हैं और इसे ऑप्टिमाइज़ कर सकते हैं।
एलेक्स लोप।

@abforce कोड का एक ब्लॉक। यह कुछ और भी हो सकता था। :)
एलेक्स लोप।

4

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


4

कंपाइलर जरूरी नहीं कि पूरा प्रोग्राम देखे। मेरे पास एक प्रोग्राम हो सकता है जो एक साझा लाइब्रेरी को कॉल करता है, जो मेरे प्रोग्राम में एक फ़ंक्शन में वापस कॉल करता है जिसे सीधे नहीं कहा जाता है।

तो एक समारोह जो पुस्तकालय के सम्मान के साथ मर चुका है, उसके खिलाफ संकलित किया जा सकता है अगर उस पुस्तकालय को रनटाइम में बदल दिया गया।


3

यदि कोई कंपाइलर सभी डेड कोड को सही तरीके से खत्म कर सकता है, तो इसे एक दुभाषिया कहा जाएगा ।

इस सरल परिदृश्य पर विचार करें:

if (my_func()) {
  am_i_dead();
}

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

एक संकलक का विचार यह है कि यह केवल कोड का आंशिक विश्लेषण करता है, इस प्रकार एक अलग चल रहे वातावरण की नौकरी को सरल करता है। यदि आप एक पूर्ण विश्लेषण करते हैं, तो यह कोई संकलक नहीं है।


यदि आप संकलक को एक फ़ंक्शन c(), कहाँ c(source)=compiled code, और चल रहे वातावरण के रूप में मानते हैं r(), जहां r(compiled code)=program output, तो किसी भी स्रोत कोड के लिए आउटपुट का निर्धारण करने के लिए आपको मूल्य की गणना करना होगा r(c(source code))। यदि गणना करने c()के r(c())लिए किसी भी इनपुट के मूल्य के ज्ञान की आवश्यकता होती है , तो एक अलग की आवश्यकता नहीं है r()और c(): आप बस इस तरह i()से एक फ़ंक्शन प्राप्त कर सकते हैं ।c()i(source)=program output


2

अन्य लोगों ने रुकने की समस्या और इसके आगे की टिप्पणी की है। ये आम तौर पर कार्यों के कुछ हिस्सों पर लागू होते हैं। हालांकि यह जानना कठिन / असंभव हो सकता है कि क्या एक पूरे प्रकार (वर्ग / आदि) का उपयोग किया जाता है या नहीं।

.NET / जावा / जावास्क्रिप्ट और अन्य रनटाइम वातावरण में प्रतिबिंब के माध्यम से लोड किए जा रहे प्रकारों को रोकने के लिए कुछ भी नहीं है। यह निर्भरता इंजेक्शन फ्रेमवर्क के साथ लोकप्रिय है, और डीसर्लाइजेशन या डायनेमिक मॉड्यूल लोडिंग के चेहरे के बारे में तर्क करना भी कठिन है।

कंपाइलर को यह पता नहीं चल सकता है कि इस तरह के लोड होंगे या नहीं। उनके नाम रनटाइम पर बाहरी कॉन्फिग फाइलों से आ सकते हैं।

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


मैं जावा, और जावास्क्रिप्ट के बारे में नहीं जानता, लेकिन .NET में वास्तव में उस तरह के DI डिटेक्शन (जिसे एजेंट मूलक कहा जाता है) के लिए एक रिसर्चर प्लगइन है। बेशक, यह कॉन्फ़िगरेशन फ़ाइलों का पता लगाने में सक्षम नहीं होगा, लेकिन यह कोड में विश्वास का पता लगाने में सक्षम है (जो कि अधिक लोकप्रिय है)।
टाई

2

एक फंक्शन लें

void DoSomeAction(int actnumber) 
{
    switch(actnumber) 
    {
        case 1: Action1(); break;
        case 2: Action2(); break;
        case 3: Action3(); break;
    }
}

आप को साबित कर सकते हैं कि actnumberकभी नहीं होगा 2तो यह है कि Action2()कभी नहीं कहा जाता है ...?


7
यदि आप फ़ंक्शन के कॉलर्स का विश्लेषण कर सकते हैं, तो आप कर सकते हैं, हाँ।
abligh

2
@ बार, लेकिन संकलक आमतौर पर सभी कॉलिंग कोड का विश्लेषण नहीं कर सकते हैं। वैसे भी अगर यह हो सकता है, पूर्ण विश्लेषण के लिए सभी संभव नियंत्रण प्रवाह के अनुकरण की आवश्यकता हो सकती है, जो संसाधनों और समय की आवश्यकता के कारण लगभग हमेशा असंभव है। इसलिए, भले ही सैद्धांतिक रूप से एक सबूत मौजूद हो कि ' Action2()कभी नहीं कहा जाएगा' व्यवहार में दावे को साबित करना असंभव है - एक कंपाइलर द्वारा पूरी तरह से हल नहीं किया जा सकता है । अंतर ऐसा है कि 'एक्स में एक संख्या मौजूद है' बनाम 'हम दशमलव में संख्या X लिख सकते हैं'। कुछ एक्स के लिए उत्तरार्द्ध कभी नहीं होगा, हालांकि पूर्व सच है।
सियापान

यह एक घटिया जवाब है। अन्य उत्तर साबित करते हैं कि यह जानना असंभव है कि नहीं actnumber==2। यह जवाब केवल दावा करता है कि यह एक जटिलता बताते हुए भी कठिन है।
MSalters

1

मैं रुकने की समस्या से असहमत हूं। मैं इस तरह के कोड को मृत नहीं कहूंगा, हालांकि वास्तव में यह कभी नहीं पहुंचेगा।

इसके बजाय, विचार करें:

for (int N = 3;;N++)
  for (int A = 2; A < int.MaxValue; A++)
    for (int B = 2; B < int.MaxValue; B++)
    {
      int Square = Math.Pow(A, N) + Math.Pow(B, N);
      float Test = Math.Sqrt(Square);
      if (Test == Math.Trunc(Test))
        FermatWasWrong();
    }

private void FermatWasWrong()
{
  Press.Announce("Fermat was wrong!");
  Nobel.Claim();
}

(प्रकार और अतिप्रवाह त्रुटियों को अनदेखा करें) मृत कोड?


2
Fermat का आखिरी प्रमेय 1994 में साबित हुआ था। इसलिए आपकी पद्धति का एक सही कार्यान्वयन FermatWasWrong कभी नहीं चलेगा। मुझे संदेह है कि आपका कार्यान्वयन FermatWasWrong को चलाएगा, क्योंकि आप फ़्लोट्स की परिशुद्धता की सीमा को मार सकते हैं।
तैमूर

@ तैमिर अहा! यह कार्यक्रम Fermat के अंतिम प्रमेय का सही परीक्षण नहीं करता है; यह जो परीक्षण करता है, उसके लिए एक
प्रतिधारण

@ मिनीबिस हाँ, मुझे याद आया कि यह एक मुद्दा बनने वाली फ़्लोट्स पर सटीक होने से पहले अतिप्रवाह होगा।
तैमूर

@immibis मेरी पोस्ट के निचले भाग पर ध्यान दें: प्रकार और ओवरफ़्लो त्रुटियों को अनदेखा करें। मैं सिर्फ वही ले रहा था जो मैंने सोचा था कि एक निर्णय के आधार के रूप में एक अनसुलझी समस्या थी - मुझे पता है कि कोड सही नहीं है। यह एक समस्या है जो वैसे भी पाशविक नहीं हो सकती।
लोरेन Pechtel

-1

इस उदाहरण को देखें:

public boolean isEven(int i){

    if(i % 2 == 0)
        return true;
    if(i % 2 == 1)
        return false;
    return false;
}

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


1
उम्म, सच में? अगर मैं लिखता हूं कि C # + ReSharper में मुझे कुछ संकेत मिले हैं। उनके बाद अंत में मुझे कोड देता है return i%2==0;
थॉमस वेलर

10
आपका उदाहरण समझाने के लिए बहुत सरल है। के विशिष्ट मामले में i % 2 == 0और i % 2 != 0पूर्णांक मोड्यूलो के मान के बारे में तर्क की आवश्यकता नहीं है एक स्थिर (जो अभी भी करना आसान है), इसके लिए केवल सामान्य उपसंचाई उन्मूलन और सामान्य सिद्धांत (विहितकरण, यहां तक ​​कि) की आवश्यकता होती है जिसे if (cond) foo; if (!cond) bar;सरल बनाया जा सकता हैif (cond) foo; else bar; । बेशक "शब्दार्थ को समझना" एक बहुत ही कठिन समस्या है, लेकिन यह पोस्ट न तो यह दिखाती है कि यह है, न ही यह दर्शाता है कि मृत कोड पहचान के लिए इस कठिन समस्या को हल करना आवश्यक है।

5
आपके उदाहरण में, एक अनुकूलन करने वाला कंपाइलर आम सबप्रेप्रेशन को स्पॉट करेगा i % 2और इसे एक अस्थायी चर में खींच लेगा । इसके बाद यह पहचाना जाएगा कि दो ifकथन परस्पर अनन्य हैं और इन्हें लिखा जा सकता है if(a==0)...else..., और फिर यह सुनिश्चित करें कि सभी संभावित निष्पादन मार्ग पहले दो returnकथनों से गुजरें और इसलिए तीसरा returnकथन मृत कोड है। (ए अच्छा अनुकूलन करने वाला संकलन और भी अधिक आक्रामक है: जीसीसी ने मेरे परीक्षण कोड को बिट-हेरफेर संचालन की एक जोड़ी में बदल दिया)।
मार्क

1
यह उदाहरण मेरे लिए अच्छा है। यह उस मामले का प्रतिनिधित्व करता है जब एक संकलक को कुछ तथ्यात्मक परिस्थितियों के बारे में नहीं पता होता है। उसी के लिए जाता है if (availableMemory()<0) then {dead code}
छोटी सैंटी

1
@LittleSanti: दरअसल, GCC यह पता लगाएगी कि आपने जो कुछ भी लिखा था वह मृत कोड है! यह सिर्फ {dead code}हिस्सा नहीं है। GCC यह साबित करता है कि एक अपरिहार्य हस्ताक्षरित पूर्णांक अतिप्रवाह है। निष्पादन ग्राफ में उस चाप के सभी कोड मृत कोड हैं। जीसीसी यहां तक ​​कि सशर्त शाखा को हटा सकता है जो उस चाप की ओर जाता है।
मसलक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.