2010-07-20 28 views
11
List<int> list = ... 

for(int i = 0; i < list.Count; ++i) 
{ 
      ... 
} 

तो क्या संकलक सूची को जानता है। गणना को प्रत्येक पुनरावृत्ति कहा जाना नहीं है?क्या सी # कंपाइलर गणना गुणों को अनुकूलित करता है?

+0

यह एक चर को कॉल कर रहा है, जैसा कि अगर आपने 'int count = list.Count' किया था और इसके बजाए इसका उपयोग किया था। मैं आपके प्रश्न से उलझन में हूँ? –

+0

@ नाथन: यह वास्तव में एक संपत्ति को बुला रहा है, जो वास्तव में एक विधि कॉल है - एक परिवर्तनीय fetch नहीं। यह जेआईटी तक इसे अनुकूलित करने के लिए होगा (सी # कंपाइलर नहीं, आमतौर पर), लेकिन ऐसा नहीं होता है। –

+0

ठीक है, इस तरह के लूप का उपयोग करके, यह बहुत अच्छी तरह से हो सकता है क्योंकि आप संग्रह को संशोधित कर सकते हैं। –

उत्तर

21

क्या आप इसके बारे में निश्चित हैं?

List<int> list = new List<int> { 0 }; 

for (int i = 0; i < list.Count; ++i) 
{ 
    if (i < 100) 
    { 
     list.Add(i + 1); 
    } 
} 

तो संकलक Count संपत्ति ऊपर कैश की गई, list की सामग्री को 0 होगा और 1. यदि ऐसा नहीं किया है, सामग्री 0 से 100 के

अब, यह हो सकता है के लिए पूर्णांकों होगा आप के लिए एक प्रत्याशित उदाहरण की तरह लग रहा है; लेकिन इस बारे में क्या?

List<int> list = new List<int>(); 

int i = 0; 
while (list.Count <= 100) 
{ 
    list.Add(i++); 
} 

के रूप में अगर इन दोनों कोड के टुकड़े पूरी तरह से अलग कर रहे हैं, लेकिन यह है कि सिर्फ इसलिए कि जिस तरह से हम बनाम while छोरों for के बारे में सोचने के लिए छोरों करते हैं यह लग सकता है। किसी भी मामले में, प्रत्येक पुनरावृत्ति पर एक चर का मान चेक किया जाता है। और किसी भी मामले में, वह मूल्य बहुत अच्छी तरह से बदल सकता है।

आमतौर पर यह मानना ​​सुरक्षित नहीं है कि संकलक कुछ अनुकूलित करता है जब एक ही कोड के "अनुकूलित" और "गैर-अनुकूलित" संस्करणों के बीच व्यवहार वास्तव में अलग होता है।

+0

इससे मुझे C++ 'const' के अच्छे पुराने दिन याद आते हैं। अच्छा उदाहरण के लिए –

+1

+1 :) –

+0

आईआईआरसी, पहला उदाहरण संकलित नहीं होगा, आप इसे चालू करते समय एक सूची संशोधित नहीं कर सकते हैं। –

9

सी # कंपाइलर इस तरह के कोई अनुकूलन नहीं करता है। हालांकि, जेआईटी कंपाइलर एरे के लिए इसे अनुकूलित करता है, मुझे विश्वास है (जो आकार बदलने योग्य नहीं हैं), लेकिन सूचियों के लिए नहीं।

एक सूची की गिनती संपत्ति लूप संरचना के भीतर बदल सकती है, इसलिए यह एक गलत अनुकूलन होगा।

0

नहीं, ऐसा नहीं है। क्योंकि प्रत्येक चरण पर हालत की गणना की जाती है। यह और अधिक जटिल की तुलना में सिर्फ गिनती के साथ comparsion हो सकता है, और किसी भी बूलियन अभिव्यक्ति की अनुमति है:

for(int i = 0; new Random().NextDouble() < .5d; i++) 
    Console.WriteLine(i); 

http://msdn.microsoft.com/en-us/library/aa664753(VS.71).aspx

0

यह गणना की विशेष कार्यान्वयन पर निर्भर करता है; मैंने सूची में गिनती संपत्ति का उपयोग करने के साथ कभी भी कोई प्रदर्शन समस्या नहीं देखी है, इसलिए मुझे लगता है कि यह ठीक है।

इस मामले में आप खुद को कुछ पूछताछ के साथ कुछ टाइपिंग बचा सकते हैं।

List<int> list = new List<int>(){0}; 
foreach (int item in list) 
{ 
    // ... 
} 
+0

जब तक आप लूप बॉडी में कहीं भी सूचकांक 'i' का उपयोग नहीं करते हैं। प्रत्येक तत्व में एक जोड़ने के रूप में सरल कुछ 'foreach' के साथ नहीं किया जा सकता है। –

+0

ओपी हर तत्व में कुछ जोड़ने का जिक्र नहीं करता है? –

1

अन्य सभी टिप्पणीकर्ताओं जो कहते हैं के लिए है कि 'गणना' संपत्ति सकता है एक पाश शरीर में परिवर्तन: JIT अनुकूलन आप चल रहा है कि वास्तविक कोड का लाभ ले, की नहीं बुरी से बुरी हालत जाने क्या हो सकता है। सामान्य रूप से, गणना बदल सकती है। लेकिन यह सभी कोड में नहीं है।

तो पोस्टर के उदाहरण में (जिसमें कोई गणना-परिवर्तन नहीं हो सकता है), क्या यह पता लगाने के लिए जेआईटी के लिए अनुचित है कि लूप में कोड जो भी बदलता है, उसकी आंतरिक लंबाई सूची को बदलने के लिए उपयोग नहीं करता है? यदि यह पता चलता है कि list.Count स्थिर है, तो क्या यह लूप बॉडी से उस चरणीय पहुंच को उठाएगा?

मुझे नहीं पता कि जेआईटी यह करता है या नहीं। लेकिन मैं इस समस्या को तुच्छ रूप से बंद करने के लिए इतना तेज़ नहीं हूं "कभी नहीं।"

+0

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

+0

हां, यह निर्धारित करना मुश्किल हो सकता है। मेरी पोस्ट का मुद्दा यह है कि * कभी-कभी *, यह आसान हो सकता है। और जब यह आसान हो, तो यह अनुकूलित हो सकता है। थ्रेडिंग के लिए, यदि आप ट्रैवर्सल करते हैं तो सूची लॉक द्वारा संरक्षित नहीं होती है, यहां तक ​​कि अप्रत्याशित संस्करण भी छोटी है। – Karmastan

+0

कोई भी नहीं कहा "कभी नहीं।" ओपी ने पूछा: "क्या संकलक सूची को जानता है। गणना को प्रत्येक पुनरावृत्ति कहा जाना नहीं है?" जिस पर मैंने जवाब दिया, "क्या आप निश्चित हैं?" ऐसा लगता है कि ओपी यह मान ले रहा था कि 'गणना' संपत्ति * नहीं बदलेगी; मेरा इरादा उस धारणा को चुनौती देना था। –

3

आप आईएल पर एक नज़र दान ताओ के उदाहरण के लिए उत्पन्न नहीं उठाते, तो आप पाश की शर्त पर इस तरह की एक पंक्ति देखेंगे:

callvirt instance int32 [mscorlib]System.Collections.Generic.List`1<int32>::get_Count() 

यह निर्विवाद सबूत है कि गणना (यानी get_Count (है)) लूप के प्रत्येक पुनरावृत्ति के लिए बुलाया जाता है।

+0

मेरा x86 असेंबली कौशल बेहद जंगली है, लेकिन जेआईटी परिणामों की जांच एक 'कॉल एफएफएफएफएफएफएफएफएफ 6 बी 9 1 9 0' दिखाती है जो get_Count() – AlfredBr

+4

पर कॉल इंगित कर सकती है, नहीं, जेआईटी कंपाइलर इसे सीधे सीपीयू रजिस्टर लोड में बदल देता है। इसे "इनलाइनिंग" कहा जाता है। छोटे संपत्ति प्राप्तकर्ता हमेशा रिलीज बिल्ड में रेखांकित होते हैं। इसे देखने के लिए आपको अनुकूलित मशीन कोड पर एक नज़र रखना होगा। –

+2

ठीक है, आईएल वास्तव में क्या होता है पर अंतिम शब्द नहीं है। –

4

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

निम्नलिखित कैस पर विचार करें एसएस:

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

  • लूप के भीतर कोई भी विधि कॉल संभावित रूप से गणना के सीधे मूल्य को सीधे बदल सकता है (न केवल संग्रह पर सीधे "सीधे" जोड़ें, लेकिन लूप में कहा जाने वाला एक उपयोगकर्ता विधि संग्रह पर भी हो सकता है)

  • समसामयिक रूप से निष्पादित होने वाला कोई भी अन्य धागा गणना मूल्य भी बदल सकता है।

जेआईटी सिर्फ "पता" नहीं कर सकता कि गणना स्थिर है।

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

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

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

इसलिए, आईएमएचओ इस तरह के मामलों पर विचार करना/समझना दिलचस्प है, लेकिन आखिरकार आपको वास्तव में जानने की आवश्यकता नहीं है। ज्ञान का एक छोटा सा खतरनाक चीज हो सकता है। बस जेआईटी को अपनी बात करने दें, और फिर नतीजे देखने के लिए परिणाम देखें कि क्या इसे सुधारने की जरूरत है या नहीं।

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

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