6

有人说:“任何可以通过数组下标实现的操作,也可以用指针来完成,指针版本一般会更快”。

我怀疑上面的结果,所以我做了以下测试:</p>

在下面的文章中,我们不关心编译器优化。关于编译器优化如何影响指针和数组之间的效率,请注意:效率:数组与指针

(Visual Studio 2010,调试模式,无优化)

#include <windows.h>
#include <stdio.h>

int main()
{
    int a[] = {10,20,30};
    int* ap = a;

    long counter;

    int start_time, end_time;
    int index;

    start_time = GetTickCount();
    for (counter = 1000000000L; counter>0; counter--)
    {
        *(ap+1) = 100;
    }
    end_time = GetTickCount();
    printf("10 billion times of *ap = %d\n", end_time-start_time);

    start_time = GetTickCount();
    for (counter = 1000000000L; counter>0; counter--)
    {
        a[1] = 101;
    }
    end_time = GetTickCount();
    printf("10 billion times of a[0] = %d\n", end_time-start_time);

    return 0;
}

结果是:</p>

10 billion times of *ap = 3276
10 billion times of a[0] = 3541

指针似乎有点快。但是对比了拆机后,我陷入了更深的困惑。</p>

(Visual Studio 2010,调试模式,无优化)

; 17   :         *(ap+1) = 100;
mov eax, DWORD PTR _ap$[ebp]
mov DWORD PTR [eax+4], 100          ; 00000064H

; 25   :         a[1] = 101;
mov DWORD PTR _a$[ebp+4], 101       ; 00000065H

从汇编输出来看,通过指针访问内存需要 2 条指令,而数组只需要 1 条指令。

为什么数组执行的指令更少,但它所花费的时间并不比指针少?

它与cpu缓存相关联吗?如何修改我的测试代码来证明它?

4

1 回答 1

2

首先也是最重要的,C 语言没有速度。这是 C 的实现引入的一个属性。例如,C 没有速度,但 GCC 编译器生成的代码的速度可能与 Clang 编译器生成的代码不同,而且它们都可能生成的代码超出 -执行由 Cint 或 Ch 解释器产生的行为。所有这些都是 C 实现。其中一些比其他的慢,但速度无论如何不能归因于 C

C标准的6.3.2.1说:

除非它是 sizeof 运算符、_Alignof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串文字,否则类型为 ''array of type'' 的表达式将转换为具有type ''pointer to type'' 指向数组对象的初始元素并且不是左值。

这应该表明您的代码*(ap+1)a[1]代码中都是指针操作。此翻译将在 Visual Studio 的编译阶段发生。因此,这不应该对运行时产生任何影响。

关于“数组下标”的6.5.2.1说:

其中一个表达式应具有类型“指向完整对象类型的指针”,另一个表达式应具有整数类型,并且结果具有类型“类型”。这似乎表明数组下标运算符实际上是一个指针运算符......

正如我们之前假设的,这ap[1]确实是一个指针操作的确认。然而,在运行时,数组已经被转换为指针。性能应该相同。

……那么,为什么它们不一样?

您使用的操作系统有什么特点?它不是一个多任务、多用户的操作系统吗?假设操作系统要在不中断的情况下完成第一个循环,然后中断第二个循环并将控制权切换到不同的进程。这种中断不会使您的实验无效吗?您如何衡量任务切换引起的中断频率和时间?请注意,这对于不同的操作系统会有所不同,并且操作系统是实现的一部分。

你使用的CPU有什么特点?它是否有自己的快速内部机器代码缓存?假设你的整个第一个循环,它包含的计时机制很好地适合代码缓存,但第二个循环被截断了。这不会导致缓存未命中,并且在您的 CPU 从 RAM 中获取剩余代码时等待很长时间吗?您如何测量由缓存未命中引起的中断时间?请注意,这对于不同的 CPU 会有所不同,并且 CPU 是实现的一部分。

这些问题应该引发一些问题,例如“这个微优化基准是否解决了有意义或重要的问题?”。优化的成功取决于问题的大小和复杂性。找到一个重要问题,解决它,分析解决方案,优化它并再次分析它。这样,您可以提供有关优化版本的速度有多快的有意义的信息。你的老板会对你更满意,只要你不透露优化可能只与你的实施相关,正如我之前提到的。我敢肯定,您会发现最不担心的是数组取消引用与指针取消引用

于 2013-04-05T15:13:02.470 回答