首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ILGenerator方法内联

ILGenerator方法内联
EN

Stack Overflow用户
提问于 2011-12-31 08:05:24
回答 3查看 1.4K关注 0票数 8

给定以下代码:

代码语言:javascript
复制
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内联吗?

EN

回答 3

Stack Overflow用户

发布于 2012-01-01 00:15:08

经过进一步的研究,我得出了以下结论:

  1. ILGenerator生成的方法永远不会内联。无论是使用委托、从另一个DynamicMethod还是从使用MethodBuilder.
  2. Existing方法创建的方法调用它们(在C#中编码并由VS编译的方法)只有在从使用MethodBuilder创建的方法调用时才能内联。如果从DynamicMethod调用,它们将永远不会被内联。

在彻底测试了许多示例并查看了最终的汇编程序代码后,我得出了这个结论。

票数 3
EN

Stack Overflow用户

发布于 2011-12-31 08:17:15

如果我没理解错的话,我猜是因为这个方法是动态生成的,所以JIT编译器不知道为调用者内联它。

我已经写了大量的IL,但我没有研究内联行为(主要是因为动态方法通常对于我的目的来说已经足够快了,而不需要进一步的优化)。

我欢迎在这个问题上更有见地的人给出反馈(请不要只是否决;如果我错了,我想在这里学到一些东西)。

非动态

  • 您可以编写“普通”.NET代码(例如C#、VB.NET、任何支持CLS的语言)
  • IL在编译时创建
  • 机器码在运行时创建;方法在适当的

处内联

动态

  • 你写“普通”的.NET代码,其目的是创建一个动态方法
  • IL是在编译时为这段代码创建的,但该动态方法不是在运行时为生成动态方法的代码创建的
  • 当该代码被调用时,动态方法被创建为IL在一个特殊的JIT方法的IL被编译到机器上时
  • 编译器不会重新编译其他调用者来内联这个新的动态方法。或者其他调用者本身也是动态的,尚未创建。
票数 0
EN

Stack Overflow用户

发布于 2012-01-01 04:48:42

您可以尝试生成动态程序集。我的理解是,大多数运行时并不知道它是动态的。我认为在内部它会像任何其他byte[]程序集一样被加载。因此,JIT/inliner也可能不知道(或者不关心)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8685263

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档