use*_*648 11 c arrays indexing pointers
为什么数组索引比指针快?指针不应该比数组索引快吗?
**我使用time.h clock_t来测试两个函数,每个函数循环200万次.
Pointer time : 0.018995
Index time : 0.017864
void myPointer(int a[], int size)
{
int *p;
for(p = a; p < &a[size]; p++)
{
*p = 0;
}
}
void myIndex(int a[], int size)
{
int i;
for(i = 0; i < size; i++)
{
a[i] = 0;
}
}
Run Code Online (Sandbox Code Playgroud)
数组取消引用p[i]
是*(p + i)
. 编译器利用在 1 或 2 个周期内执行数学 + 解引用的指令(例如 x86 LEA 指令)来优化速度。
使用指针循环,它将访问和偏移量分成单独的部分,编译器无法对其进行优化。
可能是 for 循环中的比较导致了差异。每次迭代都会测试终止条件,并且您的“指针”示例具有稍微复杂一些的终止条件(取 &a[size] 的地址)。由于 &a[size] 不会改变,您可以尝试将其设置为变量以避免在循环的每次迭代中重新计算它。
不,永远不会指针比数组索引更快.如果其中一个代码比另一个快,那主要是因为某些地址计算可能不同.该问题还应该提供编译器和优化标志的信息,因为它会严重影响性能.
上下文中的数组索引(数组绑定未知)与指针操作完全相同.从编译器的角度来看,它只是指针算法的不同表达.以下是Visual Studio 2010中优化的x86代码示例,该代码具有完全优化且无内联.
3: void myPointer(int a[], int size)
4: {
013E1800 push edi
013E1801 mov edi,ecx
5: int *p;
6: for(p = a; p < &a[size]; p++)
013E1803 lea ecx,[edi+eax*4]
013E1806 cmp edi,ecx
013E1808 jae myPointer+15h (13E1815h)
013E180A sub ecx,edi
013E180C dec ecx
013E180D shr ecx,2
013E1810 inc ecx
013E1811 xor eax,eax
013E1813 rep stos dword ptr es:[edi]
013E1815 pop edi
7: {
8: *p = 0;
9: }
10: }
013E1816 ret
13: void myIndex(int a[], int size)
14: {
15: int i;
16: for(i = 0; i < size; i++)
013E17F0 test ecx,ecx
013E17F2 jle myIndex+0Ch (13E17FCh)
013E17F4 push edi
013E17F5 xor eax,eax
013E17F7 mov edi,edx
013E17F9 rep stos dword ptr es:[edi]
013E17FB pop edi
17: {
18: a[i] = 0;
19: }
20: }
013E17FC ret
Run Code Online (Sandbox Code Playgroud)
一目了然,myIndex
看起来更快,因为指令的数量较少,但是,这两段代码基本相同.两者最终都使用rep stos
,这是x86的重复(循环)指令.唯一的区别是因为循环边界的计算.在for
中环myIndex
有行程计数size
,因为它是(即,不需要计算).但是,myPointer
需要一些计算来获得for
循环的行程计数.这是唯一的区别.重要的循环操作是一样的.因此,差异可以忽略不计.
总而言之,优化代码的性能myPointer
和myIndex
优化代码应该相同.
FYI,如果数组的绑定在编译时是已知的,例如int A[constant_expression]
,那么对该数组的访问可能比指针1快得多.这主要是因为数组访问没有指针分析问题.编译器可以完美地计算固定大小数组上的计算和访问的依赖性信息,因此它可以进行高级优化,包括自动并行化.
但是,如果计算是基于指针的,则编译器必须执行指针分析以进一步优化,这在C/C++中非常有限.它通常在指针分析上得到保守的结果,并导致一些优化机会.