【问题标题】:Why are first few string assignments slower?为什么前几个字符串分配速度较慢?
【发布时间】:2014-07-03 13:46:36
【问题描述】:

我一直在用下面的代码测量初始化字符串的速度,发现有些奇怪:

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

#define START_COUNTING  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start)
#define END_COUNTING    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end)
#define NUM_OF_LOOPS    8192

long time_difference(struct timespec end, struct timespec beginning) {
    long long diff = (long long)end.tv_sec - (long long)beginning.tv_sec;
    return(end.tv_nsec - beginning.tv_nsec + ((diff > 0) ? 1000000L : 0));
}

long average(long numbers[NUM_OF_LOOPS]) {
    long long sum = 0;
    int i;

    for(i = 0; i < NUM_OF_LOOPS; i++)
        sum += numbers[i];

    return sum / NUM_OF_LOOPS;
}

int main(void) {
    struct timespec start;
    struct timespec end;
    long time_diffs[NUM_OF_LOOPS];
    int i;
    char * str = NULL;

    for(i = 0; i < NUM_OF_LOOPS; i++) {
        START_COUNTING;
        str = "T";
        END_COUNTING;
        time_diffs[i] = time_difference(end, start);
    }
    printf("%li ns - assigning 2 chars to a string\n", average(time_diffs));

    for(i = 0; i < NUM_OF_LOOPS; i++) {
        START_COUNTING;
        str = "Testing";
        END_COUNTING;
        time_diffs[i] = time_difference(end, start);
    }
    printf("%li ns - assigning 8 chars to a string\n", average(time_diffs));

    for(i = 0; i < NUM_OF_LOOPS; i++) {
        START_COUNTING;
        str = "Testing it here";
        END_COUNTING;
        time_diffs[i] = time_difference(end, start);
    }
    printf("%li ns - assigning 16 chars to a string\n", average(time_diffs));

    for(i = 0; i < NUM_OF_LOOPS; i++) {
        START_COUNTING;
        str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit nullam.";
        END_COUNTING;
        time_diffs[i] = time_difference(end, start);
    }
    printf("%li ns - assigning 64 chars to a string\n", average(time_diffs));

    for(i = 0; i < NUM_OF_LOOPS; i++) {
        START_COUNTING;
        str = ""; // String omitted because it would waste a lot of space
        END_COUNTING;
        time_diffs[i] = time_difference(end, start);
    }
    printf("%li ns - assigning 1024 chars to a string\n", average(time_diffs));

    return 0;
}

在分析它时,我总是想出这个结果:

1126 ns - assigning 2 chars to a string
828 ns - assigning 8 chars to a string
832 ns - assigning 16 chars to a string
834 ns - assigning 64 chars to a string
857 ns - assigning 1024 chars to a string

无论是分配 2 个字符、8 个字符还是很多,第一个结果总是比其余的要慢得多。我曾尝试在循环之前放置 1 个作业,但结果仍然没有太大变化。

有谁知道为什么分配到字符串的前几次比较慢?我环顾四周,但只找到了由于 JIT 编译器而发生的答案,但 C 不使用这些。

【问题讨论】:

  • CPU 缓存可能吗?按需加载数据?
  • 字符数无关紧要 - 在任何情况下它只是分配一个指针。函数的第一次执行可能会比较慢,因为需要的数据应该被加载到内存中。

标签: c string performance initialization


【解决方案1】:

您的代码实际上并没有复制任何字符数据,因为您只是设置了一个指向字符串文字的指针。

因此,整个前提是错误的,并且“分配”的字符数与所用时间之间没有相关性。

这是一个超微基准,而且非常难以正确,因为您实际上是在尝试测量单个指针大小分配所花费的时间,这实际上并没有很多代码。

速度差异可能是由缓存/内存延迟引起的。您可以阅读生成的代码来确定str 是否存储在寄存器中,当然也可以使用优化设置。

注意:您的测量方法,平均,意味着开销的缓存占用空间(time_diffs 数组)比测试本身的缓存占用空间大很多,所以这可能是导致不规则的原因, 第一次time_diffs 没有被缓存。

【讨论】:

  • 我从未说过我的代码会复制任何数据。这有什么关系?
  • @TheRedFox 你说“不管是分配 2 个字符、8 个字符还是很多”。
【解决方案2】:

这里有两个重要的项目:-

  • 不要使用调试代码来计时
  • 确保您确实在计时

第一个很明显,第二个不是。例如这段代码:-

for(i = 0; i < NUM_OF_LOOPS; i++) {
    START_COUNTING;
    str = "T";
    END_COUNTING;
    time_diffs[i] = time_difference(end, start);
}

很可能会被优化编译器编译成这个:-

for(i = 0; i < NUM_OF_LOOPS; i++) {
    START_COUNTING;
    END_COUNTING;
    time_diffs[i] = time_difference(end, start);
}
str = "T";

第一个循环慢的原因,可能是缓存。 cpu 必须从 RAM/HD 加载代码,这需要一段时间,尤其是当你在那里有一个函数调用时。试着把它放在计时循环之前:-

    START_COUNTING;
    END_COUNTING;

希望代码被带入缓存,为计时循环做好准备。

【讨论】:

    猜你喜欢
    • 2013-10-05
    • 2011-06-03
    • 1970-01-01
    • 2019-10-14
    • 2021-06-15
    • 2021-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多