【问题标题】:Why is processing an array for the second time slower?为什么第二次处理数组变慢了?
【发布时间】:2017-11-14 04:03:22
【问题描述】:

这个简单的 C 代码首先创建一个包含 0xFFFFFF 元素的数组,然后将其传递两次,测量每次传递所花费的时间:

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

#define TESTSIZ 0xffffff

char testcases[TESTSIZ];

void gentestcases(void)
{
        size_t i = 0;
        while(i < TESTSIZ)
                testcases[i++] = rand()%128;

        return;
}

long long time_elapsed(struct timespec beg, struct timespec end)
{
        if(end.tv_nsec < beg.tv_nsec) {
                end.tv_nsec += 1000000000;
                end.tv_sec--;
        }

        return 1000000000ll*(end.tv_sec-beg.tv_sec) + end.tv_nsec-beg.tv_nsec;
}

long long test( int(*func)(int) )
{
        struct timespec beg, end;

        clock_gettime(CLOCK_MONOTONIC, &beg);

        int volatile sink;
        size_t i = 0;
        while(i < TESTSIZ)
                sink = islower(testcases[i++]);

        clock_gettime(CLOCK_MONOTONIC, &end);

        return time_elapsed(beg, end);
}

int main()
{
        gentestcases();

        struct timespec beg, end;

        printf("1st pass took %lld nsecs\n", test(islower));
        printf("2nd pass took %lld nsecs\n", test(islower));
}

我用gcc -O2 -std=gnu89 -o sb sillybench.c编译它

通常我得到的结果是第二次处理数组比较慢。效果很小但很明显(1-3 毫秒)并且 - 除了一个例外 - 重复:

m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13098789 nsecs
2nd pass took 13114677 nsecs
m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13052105 nsecs
2nd pass took 13134187 nsecs
m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13118069 nsecs
2nd pass took 13074199 nsecs
m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13038579 nsecs
2nd pass took 13079995 nsecs
m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13070334 nsecs
2nd pass took 13324378 nsecs
m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13031000 nsecs
2nd pass took 13167349 nsecs
m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13019961 nsecs
2nd pass took 13310211 nsecs
m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13041332 nsecs
2nd pass took 13311737 nsecs
m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13030913 nsecs
2nd pass took 13177423 nsecs
m@m-X555LJ ~/UVA/fastIO $ ./sb
1st pass took 13060570 nsecs
2nd pass took 13387024 nsecs

为什么会这样?如果有的话,我想第一次处理数组应该更慢,而不是第二次!

如果这很重要:

m@m-X555LJ ~/UVA/fastIO $ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

System:    Host: m-X555LJ Kernel: 4.4.0-21-generic x86_64 (64 bit gcc: 5.3.1)
           Desktop: Cinnamon 3.0.7 (Gtk 2.24.30) Distro: Linux Mint 18 Sarah

CPU:       Dual core Intel Core i5-5200U (-HT-MCP-) cache: 3072 KB
           flags: (lm nx sse sse2 sse3 sse4_1 sse4_2 ssse3 vmx) bmips: 8786
           clock speeds: max: 2700 MHz 1: 2200 MHz 2: 2202 MHz 3: 2200 MHz
           4: 2200 MHz

【问题讨论】:

  • 有趣的笔记,我剪切/粘贴了您的代码并得到了相同的结果。但是当我将函数指针参数删除到test 时,由于您没有使用它,我得到了您期望的结果,即第二次运行更快。
  • 有趣。我正在使用 Visual Studio 进行测试,它对两次传递几乎完全相同的程序集进行内联。我猜这个差异是由于一些缓存魔法造成的。
  • -std=gnu99-std=gnu11编译有什么区别吗?您是否尝试过第三次运行?
  • “问题”当然与 rand 使用无关,因为措施(test 函数)不要使用它......它更像是分辨率测量问题和/或操作系统分页问题。
  • 1) 在繁忙的系统上:调度。在第一个循环之后,该进程已用完操作系统分配给它的时间。在第二个循环的执行期间,它的调度较少。 2) 在空闲系统上:热效应。在第一个循环期间,该过程会导致 CPU 升温,因此在第二个循环期间降低了最大可能的 CPU 频率。

标签: c arrays performance


【解决方案1】:

这种影响很可能是由 turbo 模式(或 Intel Turbo Boost 技术)引起的。 Turbo 模式允许处理器内核以高于标称时钟频率运行。其中一个因素是时间爆发*。通常在几分之一秒内,处理器将达到最高频率。很有可能第一个循环运行的时钟频率高于第二个循环。

您可以通过手动设置标称频率(处理器为 2.20 GHz)来确认这一点,例如通过using cpufrequtilscpupower。然而,在许多系统上使用intel_pstate,它不允许用户空间管理器。以下是您可以将disable turbo mode for intel_pstate - 或disable intel_pstate 一起使用的方法。

如果没有涡轮模式,性能应该是一致的。

*:温度是另一个因素,但我怀疑它在 10 毫秒的基准测试时间内是否起作用。举例来说,假设 CPU 超过 15 W TDP 并使用 20 W:即使是 1 g 的微小铜也只能heat up by 0.5 K after 10 ms。我通常会看到一个短暂的明显爆发(时间,几十毫秒到几秒),然后是缓慢而稳定的下降(温度,几十秒到几分钟)

注意:gentestcases 在有助于处理器“冲刺”的第一次实际测试之前运行很长一段时间(例如 240 毫秒)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-11
    相关资源
    最近更新 更多