use*_*808 7 .net methods cil inline 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
调用方法Do得到内联.循环在大约40毫秒内完成.例如,如果我将Do设为虚函数,则不会内联,并且循环在240 ms内完成.到现在为止还挺好.当我使用ILGenerator生成Do方法(Echo),然后使用与给定main方法相同的循环生成DynamicMethod时,调用Echo方法永远不会内联,并且循环完成需要大约240 ms.MSIL代码是正确的,因为它返回与C#代码相同的结果.我确信方法内联是由JIT完成的,所以我认为没有理由不内联Echo方法.
有谁知道为什么这个简单的方法不会被JIT内联.
如果我理解正确的话,我的猜测是,由于该方法是动态生成的,JIT 编译器不知道为调用者内联它。
我已经编写了大量的 IL,但我还没有研究过内联行为(主要是因为动态方法对于我的目的来说通常足够快,无需进一步优化)。
我欢迎对此主题更了解的人提供反馈(请不要只是否决;如果我错了,我想在这里学到一些东西)。
非动态
动态的