整数の配列があり、それをループしています:
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 コンパイラによって生成されたコードと同じ速度で実行されます。