0

当我简单地包装 DataStructure 调用时,我看不到 AggressiveInlining 的效果

例如:

public class WList
{
   //made static just to check inlining
   //otherwise inlining might not kick in since 'this' keyword will disallow it
   static System.Collections.ArrayList list;

   [MethodImpl(MethodImplOptions.AggressiveInlining)]
   public override void Put(object item)
   {
       WList.list.Add(item);
   }
}

然后这样做

    //Add 1000000 number of items in the list
    //Run this test many times 
    for (int j = 0; j < 100; j++)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();

        for (int i = 0; i < length; i++)
        {
            //ADD ITEMS IN A SIMPLE ARRAY LIST
            arraylist.Add(emptyobject);
        }

        sw.Stop();
        //clear the list
    }

    //Do similar test on wrapper
    for (int j = 0; j < 100; j++)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();

        for (int i = 0; i < length; i++)
        {
            //ADD ITEMS IN ARRAY LIST THROUGH WRAPPER
            wrapperList.Put(emptyobject);
        }

        sw.Stop();
        //clear the list
    }

Inlinig 基本上应该解开函数并将内部代码放在外面,但是我从包装器中获得的数字比直接调用要慢。

如果使用 Hastable 或使用 get 调用而不是 add,则数字保持不变。

函数调用的性能成本仍然存在。

4

2 回答 2

5

秒表不是评估代码是否被内联的正确工具。它只是提供了一个性能指标。

首先,要内联代码,您必须执行启用优化的构建。使用 Visual Studio 执行此操作的最简单方法是使用发布版本。JITter 不会优化调试版本,因此不会内联任何内容。要查看代码是否已被 JITter 内联,我建议您附加调试器并比较调试构建和发布构建之间的反汇编。如果调用是内联的,那么您会注意到不同之处。

在您的示例中,call对应的汇编指令WList.Put()将在发布版本中消失,并被call对应的汇编指令替换ArrayList.Add()

值得一提的是,默认情况下,当您将 Visual Studio 调试器附加到任何构建时,它会抑制优化以允许您正确单步执行代码的每一行。要关闭此功能,您需要取消选中工具 -> 选项 -> 调试 -> 常规中的“在模块加载时抑制 JIT 优化”选项。

其次,回答你的实际问题。该方法不会被内联,因为它是虚拟的。请参阅下面的博客文章。

http://blogs.msdn.com/b/davidnotario/archive/2004/11/01/250398.aspx

“这些是我们不会内联方法的一些原因:

  • 虚拟呼叫:我们不会跨虚拟呼叫内联。不这样做的原因是我们不知道调用的最终目标。我们可能会在这里做得更好(例如,如果 99% 的调用最终在同一个目标中,您可以生成代码来检查虚拟调用将要执行的对象的方法表,如果它不是99% 的情况下,你执行一个调用,否则你只执行内联代码),但与 J 语言不同,我们支持的主要语言中的大多数调用都不是虚拟的,所以我们不会被迫如此激进优化这个案例。”

虽然这篇文章已有近 10 年的历史,但我认为这不会随着任何后来的 JIT 编译器而改变。

于 2013-07-25T08:07:09.123 回答
2

因为您的方法是虚拟的,JIT 编译器不会内联虚拟调用,请阅读此论坛了解更多信息。还要确保方法是内联的,您可以:

检查 System.Reflection.MethodBase.GetCurrentMethod().Name。如果方法是内联的,它将返回调用者的名称。

于 2013-07-25T07:57:01.630 回答