2011-12-31 14 views
6

कोड निम्नलिखित को देखते हुए:ILGenerator विधि को इनलाइन

using System; 
using System.Reflection.Emit; 
using System.Diagnostics; 
using System.Reflection; 

namespace ConsoleApplication1 
{ 
    class A 
    { 
     public int Do(int n) 
     { 
      return n; 
     } 
    } 

    public delegate int DoDelegate(); 

    class Program 
    { 
     public static void Main(string[] args) 
     { 
      A a = new A(); 

      Stopwatch stopwatch = Stopwatch.StartNew(); 
      int s = 0; 
      for (int i = 0; i < 100000000; i++) 
      { 
       s += a.Do(i); 
      } 

      Console.WriteLine(stopwatch.ElapsedMilliseconds); 
      Console.WriteLine(s); 


      DynamicMethod dm = new DynamicMethod("Echo", typeof(int), new Type[] { typeof(int) }, true); 
      ILGenerator il = dm.GetILGenerator(); 

      il.Emit(OpCodes.Ldarg_0); 
      il.Emit(OpCodes.Ret); 

      DynamicMethod dm2 = new DynamicMethod("Test", typeof(int), new Type[0]); 
      il = dm2.GetILGenerator(); 


      Label loopStart = il.DefineLabel(); 
      Label loopCond = il.DefineLabel(); 

      il.DeclareLocal(typeof(int)); // i 
      il.DeclareLocal(typeof(int)); // s 

      // s = 0; 
      il.Emit(OpCodes.Ldc_I4_0); 
      il.Emit(OpCodes.Stloc_1); 

      // i = 0; 
      il.Emit(OpCodes.Ldc_I4_0); 
      il.Emit(OpCodes.Stloc_0); 

      il.Emit(OpCodes.Br_S, loopCond); 

      il.MarkLabel(loopStart); 

      // s += Echo(i); 
      il.Emit(OpCodes.Ldloc_1); // Load s 
      il.Emit(OpCodes.Ldloc_0); // Load i 
      il.Emit(OpCodes.Call, dm); // Call echo method 
      il.Emit(OpCodes.Add); 
      il.Emit(OpCodes.Stloc_1); 

      // i++ 
      il.Emit(OpCodes.Ldloc_0); 
      il.Emit(OpCodes.Ldc_I4_1); 
      il.Emit(OpCodes.Add); 
      il.Emit(OpCodes.Stloc_0); 

      il.MarkLabel(loopCond); 

      // Check for loop condition 
      il.Emit(OpCodes.Ldloc_0); 
      il.Emit(OpCodes.Ldc_I4, 100000000); 
      il.Emit(OpCodes.Blt_S, loopStart); 

      il.Emit(OpCodes.Ldloc_1); 
      il.Emit(OpCodes.Ret); 


      DoDelegate doDel = (DoDelegate)dm2.CreateDelegate(typeof(DoDelegate)); 
      s = doDel.Invoke();  // Dummy run to force JIT 


      stopwatch = Stopwatch.StartNew(); 
      s = doDel.Invoke(); 
      Console.WriteLine(stopwatch.ElapsedMilliseconds); 
      Console.WriteLine(s); 
     } 
    } 
} 

विधि के लिए कॉल inlined हो जाता है। लूप लगभग 40 एमएस में खत्म होता है। यदि मैं, उदाहरण के लिए, वर्चुअल फ़ंक्शन होने के लिए करता हूं, तो यह रेखांकित नहीं होता है, और लूप 240 एमएस में समाप्त होता है। अब तक सब ठीक है। जब मैं ILGenerator प्रयोग करते हैं विधि (इको) जेनरेट करना और फिर दिया मुख्य विधि के रूप में ही फंदा डाल कर DynamicMethod उत्पन्न करने के लिए, प्रतिध्वनित करने के लिए विधि inlined हो जाता है कभी नहीं कहते हैं, और यह एक पाश समाप्त करने के लिए के लिए 240 के बारे में एमएस लेता है। एमएसआईएल कोड सही है क्योंकि यह सी # कोड के समान परिणाम देता है। मुझे यकीन है कि विधि इनलाइनिंग कि JIT द्वारा किया जाता है कुछ है था, इसलिए मैं इसे इको विधि इनलाइन करने के लिए नहीं करने के लिए कोई कारण नहीं देखते हैं।

किसी को भी पता है क्यों इस सरल विधि अभ्यस्त JIT द्वारा inlined मिल है।

+0

क्या आप कोड भी उत्पन्न कर रहे हैं जो गतिशील रूप से जेनरेट की गई Do() विधि को कॉल करता है, या वह संकलन समय पर ज्ञात कोड है? –

+0

क्या आप पूर्ण कोड नमूना शामिल कर सकते हैं जो ILGenerator का उपयोग करता है? और, बस यह सुनिश्चित करने के लिए: क्या आप रिलीज बिल्ड ** के बिना परीक्षण कर रहे हैं ** बिना ** डीबगर संलग्न? –

+0

मैंने पोस्ट एप के लिए पूर्ण कोड देकर पोस्ट को दोबारा दिया है। मैं रिलीज बिल्ड का उपयोग करता हूं और डीबगर के बिना इसे चलाता हूं। सी # लूप के लिए विधि कॉल को रेखांकित करता है और आईएल के लिए आईएल की तुलना में काफी तेजी से चलता है। – user102808

उत्तर

0

मैं सही ढंग से समझ रहा हूँ, तो मेरा अनुमान है कि जब विधि गतिशील रूप से उत्पन्न होता है, JIT कम्पाइलर कॉल करने के लिए यह इनलाइन करने के लिए पता नहीं है।

मैंने आईएल का एक बड़ा सौदा लिखा है लेकिन मैंने इनलाइन व्यवहार में ध्यान नहीं दिया है (मुख्य रूप से क्योंकि डायनामिक विधियां आमतौर पर बिना किसी अनुकूलन के मेरे उद्देश्यों के लिए पर्याप्त तेज़ होती हैं)।

मैं किसी को प्रतिक्रिया देने के लिए विषय पर अधिक जानकारों का स्वागत करता हूं (कृपया केवल डाउनवोट न करें; अगर मैं गलत हूं तो मैं यहां कुछ सीखना चाहूंगा)।

गैर गतिशील

  • आप लिखते हैं "सामान्य" नेट कोड (जैसे सी #, VB.NET, किसी भी सीएलएस अवगत भाषा)
  • आईएल संकलन समय
  • पर बनाया गया है रनटाइम पर मशीन कोड बनाया गया है; तरीकों inlined कर रहे हैं जहां उपयुक्त हो

गतिशील

  • आप "सामान्य" नेट कोड जिसका उद्देश्य है एक गतिशील विधि
  • आईएल इस कोड के लिए संकलन समय पर बनाई गई है बनाने के लिए लिखते हैं, लेकिन गतिशील विधि नहीं बनाई गई है
  • मशीन कोड कोड के लिए रनटाइम पर बनाया गया है जो गतिशील विधि
  • उत्पन्न करता है जब उस कोड को लागू किया जाता है, गतिशील मुलाकात होड एक विशेष असेंबली में आईएल के रूप में बनाया गया है
  • गतिशील विधि आईएल मशीन कोड
  • पर संकलित किया गया है, हालांकि, जेआईटी कंपाइलर अन्य गतिशील विधि को रेखांकित करने के लिए अन्य कॉलर्स को पुन: संकलित नहीं करता है। या शायद अन्य कॉलर स्वयं गतिशील हैं और अभी तक नहीं बनाए गए हैं।
+0

मैंने फुल नमूना कोड जोड़ा है जो समस्या को पुन: उत्पन्न करता है। जैसा कि आप देख सकते हैं, मैं दो गतिशील तरीकों उत्पन्न करता हूं। एक बेहद सरल है, और केवल एक ही मूल्य को एक परम के रूप में प्राप्त करें। दूसरी विधि लूप के लिए एक सरल चलती है (शुरुआत में सी # कोड के समान)। मुझे उम्मीद नहीं है कि जेआईटी कॉल को दूसरी विधि में रेखांकित करे क्योंकि इसे एक प्रतिनिधि के माध्यम से बुलाया जाता है। Howerer, मैं उम्मीद करता हूं कि उसे दूसरी विधि के अंदर बुलाए जाने पर पहली विधि को रेखांकित करने की उम्मीद है। जब मैं इसे स्वयं रेखांकित करता हूं, तो मुझे उसी लूप के 10 गुना तेजी से निष्पादन मिलता है। – user102808

1

आगे की जांच पड़ताल करने के बाद मैं निम्नलिखित निष्कर्ष निकाला है:

  1. ILGenerator उत्पन्न तरीकों inlined मिल कभी नहीं होगा। इससे कोई फ़र्क नहीं पड़ता कि आप उन्हें किसी अन्य DynamicMethod से या MethodBuilder के साथ बनाई गई विधि से प्रतिनिधि का उपयोग करके कॉल करते हैं या नहीं। जब एक विधि MethodBuilder के साथ बनाया से कहा जाता
  2. मौजूदा तरीकों (लोगों सी # में कोडित और वी.एस. द्वारा संकलित) केवल inlined कर सकते हैं।यदि डायनेमिक मोड से कहा जाता है, तो वे कभी भी रेखांकित नहीं होंगे।

मैंने कई नमूनों का परीक्षण करने और अंतिम असेंबलर कोड को देखने के बाद इसे निष्कर्ष निकाला है।

+0

जब तक कोई अलग-अलग दिखाई देता है, मुझे लगता है कि यह एक सटीक निष्कर्ष है। गतिशील तरीकों को रेखांकित नहीं करना कभी-कभी समझ में आता है (जो मैंने अपनी प्रतिक्रिया में सुझाया है)। शायद कंपाइलर डिजाइनरों ने फैसला किया कि यह सभी मामलों के लिए नियम के रूप में इलाज करना सबसे आसान होगा। मुझे आश्चर्य है कि अभिव्यक्ति पेड़ एक ही तरीके से व्यवहार करते हैं: http://msdn.microsoft.com/en-us/library/bb397951.aspx –

+0

यह जांचना दिलचस्प होगा, हालांकि मेरे पास खेलने के लिए समय (या आवश्यकता नहीं है) अभिव्यक्ति पेड़ के साथ। – user102808

+0

निश्चित रूप से इसमें आईएल-कोड भी शामिल है और डीएल के रूप में सहेजा गया है और सहेजने के बाद लोड किया गया है? क्योंकि मुझे यकीन है कि इसमें उत्पन्न असेंबली शामिल नहीं हैं जिन्हें सहेजा गया है। वैसे भी यदि आपका उत्तर सही है तो आप इसे सही के रूप में चिह्नित कर सकते हैं। –

0

आप गतिशील असेंबली उत्पन्न करने का प्रयास कर सकते हैं। मेरी समझ यह है कि अधिकांश रनटाइम इस बात से अवगत नहीं हैं कि यह गतिशील है। मुझे लगता है कि आंतरिक रूप से यह किसी अन्य बाइट [] असेंबली की तरह लोड हो जाता है। इसलिए जेआईटी/इनलाइनर को इसके बारे में भी पता नहीं हो सकता है (या परवाह नहीं करेगा)।

+0

मैंने यह भी कोशिश की है। किसी भी तरह और किसी कारण से, गतिशील रूप से जेनरेट की गई विधि (कोई फर्क नहीं पड़ता कि आप इसे कैसे उत्पन्न करते हैं) इनलाइन नहीं होंगे। फिर भी धन्यवाद। – user102808

+0

जानना अच्छा है। । – usr

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