我有一个整数数组,我正在遍历它们:
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)的连续内存块,而程序访问元素i通过执行类似* data的操作[i] = * data [0] +(i * sizeof(int))。 (请原谅我滥用符号,但你理解我的意思。)
所以这意味着C / C ++在引用数组变量时应该没有性能增益/损失。
C#怎么样? C#有一堆额外的开销,如data.Length,data.IsSynchronized,data.GetLowerBound(),data.GetEnumerator()。
显然,C#数组与C / C ++数组不同。
那么判决是什么?我应该存储int value = data [i]并使用值,还是没有性能影响?
答案 0 :(得分:22)
你可以吃蛋糕,也可以吃。在许多情况下,抖动优化器可以轻松确定数组索引访问是安全的并且不需要检查。你问题中的任何for循环就是这样一种情况,jitter知道索引变量的范围。并且知道再次检查它是毫无意义的。
您可以看到的唯一方法是生成的机器代码。我将给出一个带注释的例子:
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编译器生成的代码一样快。
答案 1 :(得分:15)
是,由于每次访问数组的边界检查都会导致性能下降。
否,您很可能不必担心它。
是的,您可以存储该值并使用该值。不,这不是因为性能问题,而是因为它使代码更具可读性(恕我直言)。
顺便说一句,JIT编译器可能优化冗余检查,因此并不意味着你实际上会检查每次调用。不管怎样,你可能不值得花时间担心它;只是使用它,如果它成为瓶颈,你总是可以返回并使用unsafe
块。
答案 2 :(得分:3)
你已经写过两种方式。两种方式运行,测量它。然后你就会知道。
但我认为你更喜欢使用副本而不是总是直接使用数组元素,只是因为它更容易编写代码,特别是如果你有很多涉及那个的操作特别值。
答案 3 :(得分:1)
编译器只能在此处执行公共子表达式优化,如果它可以证明其他线程或循环内部调用的任何方法(包括委托)都不访问该数组,那么自己创建本地副本可能更好。 / p>
但是可读性应该是你的主要关注点,除非这个循环执行了很多次。
所有这些在C和C ++中也是如此 - 索引到数组将比访问局部变量慢。
作为旁注,您建议的优化效果不佳:value
是关键字,请选择其他变量名称。
答案 4 :(得分:0)
不太确定,但如果要多次使用它,存储值可能不会有什么坏处。您还可以使用foreach语句:)