在foreach/for循环中,有一种特殊情况可以消除边界检查。这个边界检查是指数组边界检查。
在foreach/for循环中,有一种特殊情况可以消除边界检查。这个边界检查是指数组边界检查。
for(int i = 0; i < array.Length; i++) {
...
}
循环是允许JIT安全地删除数组边界检查的工具之一(无论索引是否在[0..length-1]之间)。
使用foreach
循环遍历数组等价于使用标准的for
循环遍历数组。
编辑: 正如Robert Jeppesen指出的那样:
如果该数组是本地的,则此操作将被优化。 如果该数组可以从其他位置访问,则仍然会执行边界检查。 参考:CLR中的数组边界检查消除
谢谢!我自己不知道。
array[i]
的确切出现。因此,array[i+1]
很可能会导致数组边界检查。 - Christian Klauserreadonly
可以帮助解决问题。但您始终可以将数组引用复制到本地变量中。对于边界检查,只需要本地化引用即可(分配后数组大小不会改变)。 - Christian KlauserSealedSun是正确的。不要像在C++中那样进行优化。JIT非常聪明,可以为您做正确的事情。您始终可以以不同的方式编写循环,然后检查IL代码。
static void Main(string[] args)
{
int[] array = new int[100];
00000000 push edi
00000001 push esi
00000002 push eax
00000003 xor eax,eax
00000005 mov dword ptr [esp],eax
00000008 mov edx,64h
0000000d mov ecx,79174292h
00000012 call 49E73198
00000017 mov esi,eax
int sum = 0;
00000019 xor edx,edx
0000001b mov dword ptr [esp],edx
for(int index = 0; index < array.Length; index++)
0000001e mov edi,dword ptr [esi+4]
00000021 test edi,edi
00000023 jle 00000033
{
sum += array[index];
00000025 mov eax,dword ptr [esi+edx*4+8]
00000029 add dword ptr [esp],eax
for(int index = 0; index < array.Length; index++)
0000002c add edx,1
0000002f cmp edi,edx
00000031 jg 00000025
}
Console.WriteLine(sum.ToString());
00000033 mov esi,dword ptr [esp]
00000036 call 493765F8
0000003b push eax
0000003c mov ecx,esi
0000003e xor edx,edx
00000040 call 49E83A8B
00000045 mov edi,eax
00000047 mov edx,88h
0000004c mov ecx,1
00000051 call 49E731B0
00000056 mov esi,eax
00000058 cmp dword ptr [esi+70h],0
0000005c jne 00000068
0000005e mov ecx,1
00000063 call 4936344C
00000068 mov ecx,dword ptr [esi+70h]
0000006b mov edx,edi
0000006d mov eax,dword ptr [ecx]
0000006f call dword ptr [eax+000000D8h]
00000075 pop ecx
}
00000076 pop esi
00000077 pop edi
00000078 ret
static void Main(string[] args)
{
int[] array = new int[100];
00000000 push edi
00000001 push esi
00000002 push ebx
00000003 push eax
00000004 xor eax,eax
00000006 mov dword ptr [esp],eax
00000009 mov edx,64h
0000000e mov ecx,79174292h
00000013 call 49E73198
00000018 mov esi,eax
int sum = 0;
0000001a xor edx,edx
0000001c mov dword ptr [esp],edx
int length = array.Length;
0000001f mov ebx,dword ptr [esi+4]
for (int index = 0; index < length; index++)
00000022 test ebx,ebx
00000024 jle 0000003B
00000026 mov edi,dword ptr [esi+4]
{
sum += array[index];
00000029 cmp edx,edi <-- HERE
0000002b jae 00000082 <-- HERE
0000002d mov eax,dword ptr [esi+edx*4+8]
00000031 add dword ptr [esp],eax
for (int index = 0; index < length; index++)
00000034 add edx,1
00000037 cmp edx,ebx
00000039 jl 00000029
}
Console.WriteLine(sum.ToString());
0000003b mov esi,dword ptr [esp]
0000003e call 493765F8
00000043 push eax
00000044 mov ecx,esi
00000046 xor edx,edx
00000048 call 49E83A8B
0000004d mov edi,eax
0000004f mov edx,88h
00000054 mov ecx,1
00000059 call 49E731B0
0000005e mov esi,eax
00000060 cmp dword ptr [esi+70h],0
00000064 jne 00000070
00000066 mov ecx,1
0000006b call 4936344C
00000070 mov ecx,dword ptr [esi+70h]
00000073 mov edx,edi
00000075 mov eax,dword ptr [ecx]
00000077 call dword ptr [eax+000000D8h]
0000007d pop ecx
}
0000007e pop ebx
0000007f pop esi
00000080 pop edi
00000081 ret
00000082 call 4A12746B
00000087 int 3
顺便说一下 - 这里与foreach语句相同:
static void Main(string[] args)
{
int[] array = new int[100];
00000000 push edi
00000001 push esi
00000002 push eax
00000003 xor eax,eax
00000005 mov dword ptr [esp],eax
00000008 mov edx,64h
0000000d mov ecx,79174292h
00000012 call 49E73198
00000017 mov esi,eax
int sum = 0;
00000019 xor edx,edx
0000001b mov dword ptr [esp],edx
for(int index = 0; index < array.Length; index++)
0000001e mov edi,dword ptr [esi+4]
00000021 test edi,edi
00000023 jle 00000033
{
sum += array[index];
00000025 mov eax,dword ptr [esi+edx*4+8]
00000029 add dword ptr [esp],eax
for(int index = 0; index < array.Length; index++)
0000002c add edx,1
0000002f cmp edi,edx
00000031 jg 00000025
}
Console.WriteLine(sum.ToString());
00000033 mov esi,dword ptr [esp]
00000036 call 493765F8
0000003b push eax
0000003c mov ecx,esi
0000003e xor edx,edx
00000040 call 49E83A8B
00000045 mov edi,eax
00000047 mov edx,88h
0000004c mov ecx,1
00000051 call 49E731B0
00000056 mov esi,eax
00000058 cmp dword ptr [esi+70h],0
0000005c jne 00000068
0000005e mov ecx,1
00000063 call 4936344C
00000068 mov ecx,dword ptr [esi+70h]
0000006b mov edx,edi
0000006d mov eax,dword ptr [ecx]
0000006f call dword ptr [eax+000000D8h]
00000075 pop ecx
}
00000076 pop esi
00000077 pop edi
00000078 ret
不要在没有数据的情况下尝试优化代码。正如您所看到的,如果您不阻碍JIT,它将为您做很多工作。在进行优化之前,请使用分析器。一定要使用分析器。
详情请查看:
http://codebetter.com/blogs/david.hayden/archive/2005/02/27/56104.aspx
如果您使用for循环,并且明确引用IList.Count或Array.Length,则JIT会捕获该操作并跳过边界检查。这比预先计算列表长度更快。
我相信,在列表或数组上进行foreach循环也会在内部执行相同的操作。
Current
属性,返回集合中当前的项。这消除了使用索引访问集合中的项的需要,因此不需要额外的步骤来获取项,包括边界检查。什么?我不确定在C#中是否可能消除边界检查。如果你想要非托管代码,那就使用:
int[] array;
fixed (int * i = array)
{
while (i++)
Console.WriteLine("{0}", *i);
}
for
可能是个好主意,因为答案中也包含了对for
循环优化的参考。 - Christian Klauser