6

http://blogs.msdn.com/ericgu/archive/2004/01/29/64717.aspx中,我们了解到 C# 不会将结构作为形式参数的内联方法。这是由于对堆栈的潜在依赖,例如递归吗?如果是这样,我可以通过将 struct 参数转换为 ref 参数来受益吗?

public int Sum(int i)
{
  return array1[i] + array2[i];
}

变成:

public int Sum(ref int i)
{
  return array1[i] + array2[i];
}

编辑:我去尝试测试,但我无法在线获得任何东西。这是我尝试过的:

class Program
{
  private static string result;
  static void Main(string[] args)
  {
    Console.WriteLine(MethodBase.GetCurrentMethod().Name);
    Console.WriteLine();
    m1();
    Console.WriteLine(result);
  }
  private static void m1()
  {
    result = MethodBase.GetCurrentMethod().Name;
  }
}

它打印“m1”作为第二行,这表明它没有被内联。我构建了一个发布版本并使用 Ctrl-F5 运行它(不附加调试器)。有任何想法吗?

4

5 回答 5

6

正如乔恩所说,这是一个非常古老的帖子。我可以在以下代码中确认:

using System;
using System.Runtime.CompilerServices;

struct MyStruct
{
   public MyStruct(int p)
   {
      X = p;
   }
   public int X;

   // prevents optimization of the whole thing to a constant.
   [MethodImpl(MethodImplOptions.NoInlining)]
   static int GetSomeNumber()
   {
       return new Random().Next();
   }

   static void Main(string[] args)
   {
      MyStruct x = new MyStruct(GetSomeNumber());
      // the following line is to prevent further optimization:
      for (int i = inlinetest(x); i != 100 ; i /= 2) ; 
   }

   static int inlinetest(MyStruct x)
   {
      return x.X + 1;
   }
}

inlinetest方法是内联的。

主要方法拆解:

; set up the stack frame:
00000000  push        ebp
00000001  mov         ebp,esp 

; calls GetSomeNumber:
00000003  call        dword ptr ds:[005132D8h] 

; inlined function:
00000009  inc         eax  

; the dummy for loop:
0000000a  cmp         eax,64h 
0000000d  je          0000001B 
0000000f  sar         eax,1 
00000011  jns         00000016 
00000013  adc         eax,0 
00000016  cmp         eax,64h 
00000019  jne         0000000F 
0000001b  pop         ebp  
0000001c  ret 

我已经在 Windows 7 x64 RC 上的 x86 .NET Framework 3.5 SP1 上对此进行了测试。

struct正如我所相信的那样,使用参数内联方法本质上没有任何问题。很可能,当时的 JIT 还不够聪明。

于 2009-06-26T16:03:42.437 回答
2

是一篇更好的文章,描述了为什么某些方法不会被内联。这是一个 MS 连接反馈条目其中包含基准结果 (FWIW) 的评论

于 2009-06-26T15:45:37.120 回答
0

一点澄清。该博客没有讨论 C# 将内联或不会内联的内容。它正在讨论 JITer 将内联或不内联什么。

至于为什么 JITer 不使用结构作为形式参数的内联方法,我不知道。
但是我相对肯定的是,如果他们不使用 struct 参数内联方法,那么通过 ref 制作 struct 不会改变该决定。

于 2009-06-26T15:02:09.860 回答
0

我的第一个猜测是因为这将涉及更改为该方法创建的堆栈帧的大小。对于引用类型,(类)堆栈帧必须允许存储指针(指向堆上的对象),而对于 struct ALL,必须将 structs 原始字段添加到堆栈帧足迹。

于 2009-06-26T15:07:51.313 回答
-3

正如您所指出的,这是一个值类型与引用类型的问题。想象一下,您有一个具有值类型(例如 int)参数的函数,并且您在函数中更改了该变量。显然,这对调用函数没有副作用,因为您通过值而不是通过引用传递。

现在假设您内联了相同的代码。突然之间,您的变量在调用函数中发生了变化!不是你想要的。

您可能可以通过使用引用类型考虑内联对 by-ref 参数没有问题的相同场景来说服自己- 原始函数的更改与该函数的内联版本的更改具有相同的影响。

这就是为什么它不允许这样做,但我同意应该有某种方式告诉编译器你“保证”你的函数对值类型参数没有副作用。在很多情况下,出于性能原因可能会很好。

顺便说一句,您总是可以声明一个模块级 int 并一起绕过参数:) 丑得多,但您最终会内联有问题的函数。

无论你决定做什么,祝你好运!

编辑:我只记得 Irix(SGI 操作系统)上有一个古老的 C 编译器,它实际上有一个编译器选项,允许你“强制”内联。所以可以做到,但我同意这里的选择,选择不易出错的默认值。

于 2009-06-26T15:00:55.447 回答