С учетом следующего кода:
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);
}
}
}
Вызов метода Do становится встроенным.Цикл заканчивается примерно за 40 мс.Если я, например, сделаю Do виртуальной функцией, она не будет встроена, и цикл завершится через 240 мс.Все идет нормально.Когда я использую ILGenerator для генерации метода Do (Echo), а затем генерирую DynamicMethod с тем же циклом, что и данный основной метод, вызов метода Echo никогда не становится встроенным, и для завершения цикла требуется около 240 мс.Код MSIL правильный, так как он возвращает тот же результат, что и код C #.Я был уверен, что метод встраивания методов - это то, что делается JIT, поэтому я не вижу причин, по которым он не должен включать метод Echo.
Кто-нибудь знает, почему этот простой метод не будет встроен в JIT.