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