6

http://blogs.msdn.com/ericgu/archive/2004/01/29/64717.aspxで、C# は構造体を形式パラメーターとして持つメソッドをインライン化しないことを学びます。これは、再帰などのスタックへの潜在的な依存によるものですか? もしそうなら、このように構造体パラメータを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;
  }
}

2 行目に "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

一部のメソッドがインライン化されない理由を説明するより良い記事を次に示します。これは、ベンチマーク結果 (FWIW) を含むコメント付きの MS Connect フィードバック エントリです。

于 2009-06-26T15:45:37.120 に答える
0

私の最初の推測は、そのメソッド用に作成されたスタック フレームのサイズを変更する必要があるためです。参照型 (クラス) では、スタック フレームは (ヒープ上のオブジェクトへの) ポインターの格納を許可する必要がありますが、構造体 ALL では、構造体のプリミティブ フィールドをスタックフレームのフットプリントに追加する必要があります。

于 2009-06-26T15:07:51.313 に答える
0

少し明確にします。そのブログでは、C# がインライン化するものとしないものについては議論していません。JITer が何をインライン化するか、またはインライン化しないかについて議論しています。

JITer が構造体を形式パラメーターとして持つメソッドをインライン化しない理由については、私にはわかりません。
ただし、構造体パラメーターを使用してメソッドをインライン化しない場合、参照によって構造体を作成してもその決定は変わらないことは比較的確信しています。

于 2009-06-26T15:02:09.860 に答える
-3

ご指摘のとおり、これは値型と参照型の問題です。値型 (int など) の引数を持つ関数があり、関数内でその変数を変更するとします。明らかに、参照ではなく値で渡したため、呼び出し元の関数に副作用はありません。

同じコードをインライン化するとします。突然、呼び出し元の関数で変数が変更されています! あなたが意図したものではありません。

インライン化は by-ref パラメーターに問題がないという参照型の同じシナリオを考えることで、おそらく自分自身を納得させることができます。元の関数の変更は、その関数のインライン バージョンの変更と同じ影響を及ぼします。

それが許可されない理由ですが、関数が値型の引数に副作用がないことを「保証」することをコンパイラーに伝える何らかの方法があることに同意します。多くの場合、パフォーマンス上の理由から良いかもしれません。

ちなみに、いつでもモジュール レベルの int を宣言し、引数をすべて一緒にバイパスすることができます :) ずっと醜いですが、問題の関数をインライン展開することになります。

何をするにしても、頑張ってください!

編集: Irix (SGI オペレーティング システム) に古代の C コンパイラがあり、これには実際にこのためのコンパイラ オプションがあり、インライン化を「強制」できることを思い出しました。したがって、それは可能ですが、ここでの選択に同意し、エラーが発生しにくいデフォルトを選択します。

于 2009-06-26T15:00:55.447 に答える