【问题标题】:Why is string literal faster than a static char array?为什么字符串文字比静态字符数组快?
【发布时间】:2019-09-22 17:00:52
【问题描述】:

我进行了以下测试。

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

【问题讨论】:

  • 字符串文字是静态字符数组。你是如何编译这个程序的?
  • 在 windows 上使用 mingw gcc。
  • CHAR_ARRAY 是一个指针。您可能想尝试char REAL_ARRAY[] = "FOO";
  • 简短回答:这不是 更长的回答:您的测试不足以证明一般性能差异。对程序进行一些调整,你最终会得到相反的结果
  • 不优化编译会使基准测试毫无用处。使用-O2编译。

标签: c benchmarking


【解决方案1】:

只是一个补充答案来完成其他答案。

字符串文字不是const char *。 原因是可以重新分配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)。

【讨论】:

    【解决方案2】:

    注意:编辑为与 OP 的更改“同步”:

    也许问题在于您没有进行足够好的测试。一个体面的编译器会在 zero 时间内运行这两个循环,因为它们内部不会发生任何后果。我试过 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;
    }
    

    尝试在你的编译器/机器上做这样的事情,看看它是否会有所作为。

    【讨论】:

    • 我尝试了您的修改并使用它编辑了我的帖子,字符串文字仍然总是更快。
    • 好的,现在它使速度变得平衡了,所以结论是两种处理字符串的方法都不应该影响性能?
    • 如果您打开了编译器优化,他们不应该!没有它,这是一个单独的问题。
    【解决方案3】:

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

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

    int test=5;
    

    而在第二个中它更像

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

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

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

    【讨论】:

      猜你喜欢
      • 2011-11-10
      • 2011-06-21
      • 2012-02-09
      • 2020-12-18
      • 2017-04-14
      • 2016-12-26
      • 2021-11-16
      • 2014-05-09
      • 2012-07-16
      相关资源
      最近更新 更多