给出以下代码:
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 ms内完成。到现在为止还挺好。当我使用ILGenerator生成Do方法(Echo),然后使用与给定main方法相同的循环生成DynamicMethod时,调用Echo方法永远不会内联,并且循环完成需要大约240 ms。 MSIL代码是正确的,因为它返回与C#代码相同的结果。我确信方法内联是由JIT完成的,所以我认为没有理由不插入Echo方法。
有人知道为什么这个简单的方法不会被JIT内联。
答案 0 :(得分:2)
经过进一步调查后,我得出以下结论:
在彻底测试了许多样本并查看最终汇编代码之后,我得出了这个结论。
答案 1 :(得分:0)
如果我理解正确,我的猜测是因为该方法是动态生成的,所以JIT编译器不知道为调用者内联它。
我已经编写了大量的IL,但我没有研究内联行为(主要是因为动态方法通常足够快,无需进一步优化)。
我欢迎有更多有关该主题的人提供反馈(请不要只是低估;如果我错了,我想在这里学到一些东西。)
<强>非动态强>
<强>动态强>
答案 2 :(得分:0)
您可以尝试生成动态程序集。我的理解是大多数运行时都不知道它是动态的。我认为内部它像任何其他byte []程序集一样被加载。因此,JIT /内联商可能也没有意识到它(或者不关心)。