C# では、配列変数を参照すると遅くなりますか? 質問する

C# では、配列変数を参照すると遅くなりますか? 質問する

整数の配列があり、それをループしています:

for (int i = 0; i < data.Length; i++)
{
  // do a lot of stuff here using data[i]
}

私が行った場合:

for (int i = 0; i < data.Length; i++)
{
  int value = data[i];
  // do a lot of stuff with value instead of data[i]
}

パフォーマンスの向上/低下はありますか?

私の理解では、C/C++ 配列要素は直接アクセスされます。つまり、整数の n 要素配列には長さ n * sizeof(int) の連続したメモリ ブロックがあり、プログラムは *data[i] = *data[0] + (i * sizeof(int)) のようにして要素 i にアクセスします。(表記法の乱用をお許しください。私の言いたいことはお分かりいただけると思います。)

つまり、C/C++ では配列変数の参照によるパフォーマンスの向上/低下はないはずです。

C# についてはどうでしょうか? C# には、data.Length、data.IsSynchronized、data.GetLowerBound()、data.GetEnumerator() などの余分なオーバーヘッドが多数あります。

明らかに、C# 配列は C/C++ 配列と同じではありません。

それで、結論はどうなりますか? int value = data[i] を保存して値を操作するべきでしょうか、それともパフォーマンスに影響はないのでしょうか?

ベストアンサー1

両方を同時に実現できます。ジッター オプティマイザーが配列のインデックス アクセスが安全で、チェックする必要がないことを簡単に判断できるケースは数多くあります。質問にあるような for ループはそのようなケースの 1 つで、ジッターはインデックス変数の範囲を認識しています。そして、再度チェックしても意味がないことも認識しています。

それを確認する唯一の方法は、生成されたマシン コードを見ることです。注釈付きの例を示します。

    static void Main(string[] args) {
        int[] array = new int[] { 0, 1, 2, 3 };
        for (int ix = 0; ix < array.Length; ++ix) {
            int value = array[ix];
            Console.WriteLine(value);
        }
    }

Starting at the for loop, ebx has the pointer to the array:

            for (int ix = 0; ix < array.Length; ++ix) {
00000037  xor         esi,esi                       ; ix = 0
00000039  cmp         dword ptr [ebx+4],0           ; array.Length < 0 ?
0000003d  jle         0000005A                      ; skip everything
                int value = array[ix];
0000003f  mov         edi,dword ptr [ebx+esi*4+8]   ; NO BOUNDS CHECK !!!
                Console.WriteLine(value);
00000043  call        6DD5BE38                      ; Console.Out
00000048  mov         ecx,eax                       ; arg = Out
0000004a  mov         edx,edi                       ; arg = value
0000004c  mov         eax,dword ptr [ecx]           ; call WriteLine()
0000004e  call        dword ptr [eax+000000BCh] 
            for (int ix = 0; ix < array.Length; ++ix) {
00000054  inc         esi                           ; ++ix
00000055  cmp         dword ptr [ebx+4],esi         ; array.Length > ix ?
00000058  jg          0000003F                      ; loop

配列のインデックス付けはアドレス 00003f で行われ、ebx は配列ポインタ、esi はインデックス、8 はオブジェクト内の配列要素のオフセットです。esi 値が配列境界に対して再度チェックされないことに注意してください。これは、C コンパイラによって生成されたコードと同じ速度で実行されます。

おすすめ記事