给定以下代码:
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设为虚函数,它不会被内联,循环会在240ms内结束。到目前一切尚好。当我使用ILGenerator生成Do方法( Echo ),然后使用与给定的main方法相同的循环生成DynamicMethod时,对Echo方法的调用永远不会内联,并且一个循环需要大约240ms才能完成。MSIL代码是正确的,因为它返回与C#代码相同的结果。我确信方法内联是由JIT完成的,所以我认为没有理由不内联Echo方法。
有人知道为什么这个简单的方法不能被JIT内联吗?
发布于 2012-01-01 00:15:08
经过进一步的研究,我得出了以下结论:
在彻底测试了许多示例并查看了最终的汇编程序代码后,我得出了这个结论。
发布于 2011-12-31 08:17:15
如果我没理解错的话,我猜是因为这个方法是动态生成的,所以JIT编译器不知道为调用者内联它。
我已经写了大量的IL,但我没有研究内联行为(主要是因为动态方法通常对于我的目的来说已经足够快了,而不需要进一步的优化)。
我欢迎在这个问题上更有见地的人给出反馈(请不要只是否决;如果我错了,我想在这里学到一些东西)。
非动态
处内联
动态
发布于 2012-01-01 04:48:42
您可以尝试生成动态程序集。我的理解是,大多数运行时并不知道它是动态的。我认为在内部它会像任何其他byte[]程序集一样被加载。因此,JIT/inliner也可能不知道(或者不关心)。
https://stackoverflow.com/questions/8685263
复制相似问题