कोड निम्नलिखित को देखते हुए: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 मिल है।
क्या आप कोड भी उत्पन्न कर रहे हैं जो गतिशील रूप से जेनरेट की गई Do() विधि को कॉल करता है, या वह संकलन समय पर ज्ञात कोड है? –
क्या आप पूर्ण कोड नमूना शामिल कर सकते हैं जो ILGenerator का उपयोग करता है? और, बस यह सुनिश्चित करने के लिए: क्या आप रिलीज बिल्ड ** के बिना परीक्षण कर रहे हैं ** बिना ** डीबगर संलग्न? –
मैंने पोस्ट एप के लिए पूर्ण कोड देकर पोस्ट को दोबारा दिया है। मैं रिलीज बिल्ड का उपयोग करता हूं और डीबगर के बिना इसे चलाता हूं। सी # लूप के लिए विधि कॉल को रेखांकित करता है और आईएल के लिए आईएल की तुलना में काफी तेजी से चलता है। – user102808