据说通过指针进行的存储器访问比通过数组进行的存储器访问效率更高.
在过go ,当编译器是相对愚蠢的野兽时,这可能是真的.您只需要在高优化模式下查看gcc
的部分代码输出,就知道它不再正确.其中一些代码很难理解,但一旦你理解了,它的光辉是显而易见的.
一个好的编译器将为指针访问和数组访问生成相同的代码,您可能不应该担心这种性能级别.编写编译器的人对目标体系 struct 的了解远远超过我们这些凡人.在优化代码(算法 Select 等)时,更多地关注宏观层面,并信任工具制造商完成他们的工作.
事实上,我很惊讶编译器没有优化整个
temp = a[0];
行不存在,因为temp
在下一行用不同的值重写,而a
决不标记为volatile
.
我记得很久以前的一个城市神话,关于最新的VAX Fortran编译器(在这里显示我的年龄)的基准,它的性能比竞争对手好几个数量级.
事实证明,编译器发现基准计算的结果没有在任何地方使用,所以它优化了整个计算循环,使其被遗忘.因此,运行速度有了实质性的提高.
Update:在特定情况下,优化代码更有效的原因在于您找到位置的方式.a
将位于链接/加载时确定的固定位置,同时对其进行参考.所以a[0]
或a[any constant]
将位于一个固定的位置.
出于同样的原因,p
本身也将位于固定位置.But *p
(p
的内容)是可变的,因此需要进行额外的查找以找到正确的内存位置.
您可能会发现,将另一个变量x
设置为0(而不是const
)并使用a[x]
也会引入额外的计算.
在你的一条 comments 中,你说:
按照您的建议进行操作也会产生3条通过数组进行内存访问的指令(读取索引、提取数组元素的值、存储在TEMP中).但是我还是看不到效率.:-(
我的回答是,你很可能会看到使用指针的效率.现代编译器的任务不仅仅是找出数组操作和指针操作可以转换为相同的底层机器代码.
事实上,如果不启用优化,指针代码的效率可以达到less%.考虑下面的翻译:
int *pa, i, a[10];
for (i = 0; i < 10; i++)
a[i] = 100;
/*
movl $0, -16(%ebp) ; this is i, init to 0
L2:
cmpl $9, -16(%ebp) ; from 0 to 9
jg L3
movl -16(%ebp), %eax ; load i into register
movl $100, -72(%ebp,%eax,4) ; store 100 based on array/i
leal -16(%ebp), %eax ; get address of i
incl (%eax) ; increment
jmp L2 ; and loop
L3:
*/
for (pa = a; pa < a + 10; pa++)
*pa = 100;
/*
leal -72(%ebp), %eax
movl %eax, -12(%ebp) ; this is pa, init to &a[0]
L5:
leal -72(%ebp), %eax
addl $40, %eax
cmpl -12(%ebp), %eax ; is pa at &(a[10])
jbe L6 ; yes, stop
movl -12(%ebp), %eax ; get pa
movl $100, (%eax) ; store 100
leal -12(%ebp), %eax ; get pa
addl $4, (%eax) ; add 4 (sizeof int)
jmp L5 ; loop around
L6:
*/
从该示例中,您实际上可以看到指针示例更长,为unnecessarily so.它在不变的情况下多次将pa
加载到%eax
中,实际上在pa
和&(a[10])
之间交替使用%eax
.这里的默认优化基本上是根本不优化.
当您切换到优化级别2时,您得到的代码是:
xorl %eax, %eax
L5:
movl $100, %edx
movl %edx, -56(%ebp,%eax,4)
incl %eax
cmpl $9, %eax
jle L5
对于数组版本,以及:
leal -56(%ebp), %eax
leal -16(%ebp), %edx
jmp L14
L16:
movl $100, (%eax)
addl $4, %eax
L14:
cmpl %eax, %edx
ja L16
用于指针版本.
我不打算在这里分析时钟周期(因为它工作太多,而且我基本上很懒),但我会指出一件事.就汇编指令而言,这两个版本的代码没有太大差别,而且,考虑到现代CPU的实际运行速度,除非您执行billions个这样的操作,否则您不会注意到差别.我总是倾向于为了可读性而编写代码,只有在它成为问题时才会担心性能.
顺便说一句,你引用的那句话:
5.3指针和数组:指针版本通常会更快,但至少对新手来说,更难立即掌握.
这可以追溯到K&;R的最早版本,包括我1978年的旧版本,其中函数仍然是写的:
getint(pn)
int *pn;
{
...
}
从那时起,编译器走过了漫长的道路.