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

标签 c visual-studio-2010 assembly

<分区>

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

我对上面的结果表示怀疑,所以我做了如下测试:

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

(Visual Studio 2010, Debug模式,无优化)

#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, Debug模式,无优化)

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

最佳答案

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

C 标准的 6.3.2.1 说:

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.

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

6.5.2.1 关于“数组下标”说:

One of the expressions shall have type ‘‘pointer to complete object type’’, the other expression shall have integer type, and the result has type ‘‘type’’. This seems to indicate that the array subscript operator is actually a pointer operator...

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

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

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

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

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

关于c - 指针和数组之间的效率(更少的汇编指令不会花费更少的时间),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15835095/

相关文章:

c - 错误 : selected processor does not support ARM mode `wfi'

c - 在 C 中使用 libcurl 的单个 "init"从服务器检索数据

visual-studio-2010 - 如何在 Visual C# 项目类型节点之外创建项目模板节点?

c++ - 我如何使用 tinyxml 将 xml 属性转换为 C++ 类

assembly - 阅读有关使用双数组的 IA32 汇编代码

assembly - MASM程序集:存储浮点

c++ - 切换 C++ 函数的调用堆栈

c++ - 代码块 16 位消息

c - 这里是否需要将指针设置为 NULL 并清空 char 数组?

c# - 无法将 HTMLTidy dll (libtidy.dll) 附加到 Visual Studio