为什么字符串文字比静态字符数组快?

Why is string literal faster than a static char array?

我运行下面的测试。

charspeed.c

#include <stdio.h>
#include <time.h>

#define CHAR_COUNT 26
#define CHAR_LIST "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
static const char *CHAR_ARRAY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

#define RUN_COUNT 1000000000

#define GET_CLOCK (float)clock() / CLOCKS_PER_SEC

int main()
{
    long long int sum = 0;
    float start, end;

    start = GET_CLOCK;
    for (size_t i = 0; i < RUN_COUNT; i++)
    {
        char test = CHAR_LIST[i % CHAR_COUNT];
        sum += test; // Force the loop to run!
    }
    end = GET_CLOCK;
    printf("#define Time: %f\n", end - start);

    start = GET_CLOCK;
    for (size_t i = 0; i < RUN_COUNT; i++)
    {
        char test = CHAR_ARRAY[i % CHAR_COUNT];
        sum += test; // Must be the same as fist loop!
    }
    end = GET_CLOCK;
    printf("static const *CHAR_ARRAY Time: %f\n", end - start);
    printf("sum = %lld\n", sum); // Must access "sum" after loops!
    return 0;
}

其输出

#define Time: 1.741000
static const *CHAR_ARRAY Time: 1.868000

为什么使用#define 指令的字符串文字比预初始化的静态字符数组更快?字符串文字到底存储在哪里,为什么在块范围内访问它们会更快?

使用的编译器选项是gcc -o charspeed charspeed.c

首先,我几乎可以肯定您没有在启用完全优化的情况下编译代码。
如果你这样做,两个循环都会完全消失(没有副作用)。

考虑到优化已关闭,并且生成的代码与源代码的每个步骤完全匹配,在您的第一个循环中就像您这样做了

int test=5;

而第二个更像

int something=5;
...
int *addr=&something;
int test=*addr;

在第一种情况下,您直接访问了有趣的值,但在第二种情况下,您通过间接访问了有趣的值。

但我坚持认为,它不应该与优化代码相关。

注意:根据 OP 的更改编辑为 'synchronize':

也许问题在于您没有提供足够好的测试。一个体面的编译器将 运行 两个循环在 时间内,因为它们内部没有任何结果发生。我试过 MSVC,你的代码两次都给出了 0

然而,将循环计数增加十倍并加入一些无法优化的东西,我得到的两者时间几乎相等:

#include <stdio.h>
#include <time.h>

#define CHAR_COUNT 26
#define CHAR_LIST "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
static const char* CHAR_ARRAY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

#define RUN_COUNT 1000000000 // Increased by factor of 10!
#define GET_CLOCK (double)clock() / CLOCKS_PER_SEC

int main()
{
    long long int sum = 0;
    double start, end;

    start = GET_CLOCK;
    for (size_t i = 0; i < RUN_COUNT; i++) {
        char test = CHAR_LIST[i % CHAR_COUNT];
        sum += test; // Force the loop to run!
    }
    end = GET_CLOCK;
    printf("#define Time: %lf\n", end - start);

    start = GET_CLOCK;
    for (size_t i = 0; i < RUN_COUNT; i++) {
        char test = CHAR_ARRAY[i % CHAR_COUNT];
        sum += test; // Must be the same as fist loop!
    }
    end = GET_CLOCK;
    printf("static const *CHAR_ARRAY Time: %lf\n", end - start);
    printf("sum = %lld\n", sum); // Must access "sum" after loops!
    return 0;
}

尝试在您的 compiler/machine 上做类似的事情,看看它是否有所作为。

只是一个额外的答案来完成其他答案。

字符串文字不是 const char *。 原因是a const char * 可以重新赋值。在您的情况下,这意味着您可以 CHAR_ARRAY = "foo";.

实际上这意味着编译器不能优化处理const char *的代码而不是处理字符串文字的代码。

要解决此问题,您可以使用以下方法之一:

const char *const CHAR_ARRAY = "...";
const char CHAR_ARRAY[] = "...";

这应该保证与字符串文字相同的性能。

但是,在你的情况下,由于你将CHAR_ARRAY声明为static,这意味着编译器可以看到,只查看当前源文件,如果CHAR_ARRAY可以重新分配给。

实际上这意味着,假设启用了优化:

  • 对于字符串文字,const char *constconst char[],将生成相同的汇编代码(A)。
  • 对于非静态 const char * 将生成不同的汇编代码 (B),可能会更慢,因为它们是添加的间接寻址,并且在编译时无法知道结果。
  • 对于 static const char *,它将使用汇编代码 (A) 或 (B),具体取决于编译器是否可以证明指针可以重新分配。例如,如果您在代码中的任何位置添加一个函数 void f() { CHAR_ARRAY = "foo"; },您将禁止编译器使用程序集 (A) 和 (B)。