2012-05-16 9 views
8

मैंने गतिशील सी # कोड लिखने और संकलित करने के बारे में SO पर कई पोस्ट पढ़ी हैं। उदाहरण के लिए, this post। मैं समझता हूं कि इसे कई तरीकों से किया जा सकता है।देशी गति पर गतिशील रूप से संकलित सी # कोड चलाएं ... कैसे?

हालांकि, कोड आवेदक को कॉल करना धीमा है। मैंने एक साधारण बेंचमार्क किया, और यह देशी विधि को कॉल करने से कुछ 500 एक्स धीमी है।

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

इस बारे में जाने का सबसे आसान तरीका क्या है? गतिशील कोड को एक डीएलएल में संकलित करें और फिर इसे लोड करें? क्या यह स्मृति में किया जा सकता है?

संपादित

मैं संकलन समय के बारे में परवाह नहीं है। केवल निष्पादन

public static int Execute(int i) { return i * 2; } 

    private void button30_Click(object sender, EventArgs e) 
    { 
     CSharpCodeProvider foo = new CSharpCodeProvider(); 

     var res = foo.CompileAssemblyFromSource(
      new System.CodeDom.Compiler.CompilerParameters() 
      { 
       GenerateInMemory = true, 
       CompilerOptions = @"/optimize",      
      }, 
      @"public class FooClass { public static int Execute(int i) { return i * 2; }}" 
     ); 

     var type = res.CompiledAssembly.GetType("FooClass"); 
     var obj = Activator.CreateInstance(type); 
     var method = type.GetMethod("Execute"); 
     int i = 0, t1 = Environment.TickCount, t2; 
     //var input = new object[] { 2 }; 

     //for (int j = 0; j < 10000000; j++) 
     //{ 
     // input[0] = j; 
     // var output = method.Invoke(obj, input); 
     // i = (int)output; 
     //} 

     //t2 = Environment.TickCount; 

     //MessageBox.Show((t2 - t1).ToString() + Environment.NewLine + i.ToString()); 

     t1 = Environment.TickCount; 

     for (int j = 0; j < 100000000; j++) 
     { 
      i = Execute(j); 
     } 

     t2 = Environment.TickCount; 

     MessageBox.Show("Native: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString()); 

     var func = (Func<int, int>) Delegate.CreateDelegate(typeof (Func<int, int>), method); 

     t1 = Environment.TickCount; 

     for (int j = 0; j < 100000000; j++) 
     { 
      i = func(j); 
     } 

     t2 = Environment.TickCount; 

     MessageBox.Show("Dynamic delegate: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString()); 

     Func<int, int> funcL = Execute; 

     t1 = Environment.TickCount; 

     for (int j = 0; j < 100000000; j++) 
     { 
      i = funcL(j); 
     } 

     t2 = Environment.TickCount; 

     MessageBox.Show("Delegate: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString()); 
    } 
+0

क्या आपने संकलन समय को ध्यान में रखा है? आपका बेंचमार्क कैसा दिखता है? – Botz3000

+1

सी # बिल्ड रनटाइम निष्पादित करना सी # बिल्डिंग को निष्पादित करने से 500x गुना धीमा नहीं है। वे समान हैं हालांकि विचार करने के लिए ओवरहेड हैं। आपका बेंचमार्क कैसा है, क्या आप प्रतिबिंबित करने के लिए प्रतिबिंब। प्रवेश या कुछ कंपाइलर सेवा का उपयोग कर रहे हैं? –

+0

Invoke धीमा है, जो मेरे प्रश्न का केंद्र है: आप मूल गति पर विधि को कैसे कॉल कर सकते हैं? – IamIC

उत्तर

7

हाँ, यदि आप एक MethodInfo या एक गैर विशिष्ट Delegate के माध्यम से आह्वान है, तो यह वास्तव में धीमी गति से हो जाएगा:

संपादित करें 2, 3

यहाँ बेंचमार्क कोड मैंने लिखा है । चाल है: ऐसा न करें। विभिन्न दृष्टिकोणों:

  • व्यक्तिगत तरीकों के लिए

    , एक बुनियादी के माध्यम से जाना है लेकिन Action रूप प्रतिनिधि, इस तरह लिखा हो, या एक सामान्य कैच-ऑल, Func<object[], object> के रूप में - और Delegate.CreateDelegate का उपयोग बनाने के लिए एक प्रतिनिधि टाइप किया:

    Action doSomething = (Action)Delegate.CreateDelegate(typeof(Action), method); 
    

    इस का एक और संस्करण Expression एपीआई (जो एक .Compile() विधि है), या DynamicMethod (जो CreateDelegate() है) का प्रयोग है। मुख्य बात: आपको टाइप किया जाना चाहिए प्रतिनिधि और टाइप टाइप (.DynamicInvoke) का उपयोग करके आमंत्रित करें।

  • अधिक जटिल मामलों में जहां आप पूरे प्रकार पैदा कर रहे हैं, एक अंतरफलक आप के बारे में पता लागू करने पर विचार के लिए

    , अर्थात

    IFoo foo = (IFoo)Activator.CreateInstance(...); 
    

    फिर से; प्रारंभिक डाली (जो बहुत सस्ता है) के बाद आप सिर्फ स्थिर कोड का उपयोग कर सकते हैं:

    foo.Bar(); 
    

करो नहीं उपयोग someDelegate.DynamicInvoke(...) या someMethod.Invoke(...) यदि आप प्रदर्शन के किसी भी प्रकार के बाद कर रहे हैं।

+0

मैं आपके निष्कर्ष से पूरी तरह से सहमत हूं लेकिन आपको नहीं लगता कि 500 ​​की परिमाण का अंतर डायनामिक इनोकेशन (जैसे बेंचमार्किंग त्रुटियों) –

+1

@RuneFS की तुलना में किसी अन्य चीज़ से संबंधित है, 'विधि i * 2; '- I लगता है कि एक अक्षांश 500 की अपेक्षा करने के लिए पूरी तरह से उचित है - यानी परीक्षण किया जा रहा कोड गायब हो रहा है, और परीक्षण किया जा रहा है कि सभी गति तेज है। –

+0

@RuneFS अधिक जानकारी के लिए, यहां एक नज़र डालें: http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx – sloth

3

मार्क की सलाह के अलावा आप "अनुकूलन" संकलक विकल्प निर्दिष्ट करने के द्वारा की गति में सुधार कर सकते हैं:

var res = foo.CompileAssemblyFromSource(
     new System.CodeDom.Compiler.CompilerParameters() 
     { 
      GenerateInMemory = true, 
      CompilerOptions = "/optimize" 
     }, 
1

विचार कैसे सभी संभावित विकल्पों देखा और उनके प्रदर्शन विशेषताओं यह दिखा लायक था।

public void Test(Func<int> func) 
{   
    var watch = new Stopwatch(); 
    watch.Start(); 
    for (var i = 0; i <= 1000000; i++) 
    { 
     var test = func(); 
    } 
    Console.WriteLine(watch.ElapsedMilliseconds); 
} 

public class FooClass { public int Execute() { return 1;}} 

सेट करें और निष्पादन:

using (Microsoft.CSharp.CSharpCodeProvider foo = 
     new Microsoft.CSharp.CSharpCodeProvider()) 
{ 
    var res = foo.CompileAssemblyFromSource(
     new System.CodeDom.Compiler.CompilerParameters() 
     { 
      GenerateInMemory = true 
     }, 
     "public class FooClass { public int Execute() { return 1;}}" 
    ); 

    var real = new FooClass(); 
    Test(() => real.Execute());     // benchmark, direct call 

    var type = res.CompiledAssembly.GetType("FooClass"); 
    var obj = Activator.CreateInstance(type);  
    var method = type.GetMethod("Execute"); 
    var input = new object[] { };     
    Test(() => (int)method.Invoke(obj, input)); // reflection invoke 

    dynamic dyn = Activator.CreateInstance(type); 
    Test(() => dyn.Execute());     // dynamic object invoke 

    var action = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), null, method); 
    Test(() => action());       // delegate 
} 

परिणाम हैं:

8  // direct 
771 // reflection invoke 
41 // dynamic object invoke 
7  // delegate 
उन मामलों में जहाँ आप प्रतिनिधियों उपयोग नहीं कर सकते में

तो (निम्नलिखित सहायक वर्गों और कार्यों को देखते हुए यदि आप पर्याप्त नहीं जानते हैं?), तो आप dynamic आज़मा सकते हैं।

+0

मुझे अपने परीक्षण पर अलग-अलग समय मिलते हैं। मेरे लिए, प्रत्यक्ष 7.8 था, प्रतिनिधि 43.7 था। – IamIC

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