指针和数组之间的效率(较少的汇编指令不会花费更少的时间)

时间:2013-04-05 13:21:46

标签: c visual-studio-2010 assembly

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

我怀疑上述结果,所以我做了以下测试:

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

(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;
}

结果是:

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

指针似乎有点快。 但是在我比较了拆解之后,我陷入了更深的困惑。

(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缓存有关吗?如何修改我的测试代码以证明它?

1 个答案:

答案 0 :(得分:2)

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

C标准的6.3.2.1 说:

  

除非它是sizeof运算符的操作数,否则_Alignof   操作员,或一元&amp;运算符,或者是用于的字符串文字   初始化一个数组,一个类型为''array of type''的表达式   转换为类型为''指向类型'指针的表达式   到数组对象的初始元素,而不是左值。

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

关于“数组下标”的

6.5.2.1 说:

  

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

这是确认ap[1]确实是指针操作,正如我们之前假设的那样。但是,在运行时,数组已经转换为指针。表现应该相同。

...那么,为什么它们不相同?

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

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

这些问题应该提出一些问题,例如“这个微优化基准是否解决了一个有意义或重要的问题?”。优化的成功将根据问题的大小和复杂性而有所不同。找到一个重要问题,解决它,分析解决方案,优化它并再次对其进行分析。这样,您就可以提供有关优化版本的速度的有用信息。如前所述,如果你没有透露优化可能只与你的实现有关,那么你的老板会对你更开心。我相信你会发现你最不担心的是数组取消引用vs指针取消引用