代码之家  ›  专栏  ›  技术社区  ›  Bartvbl

是什么导致我的循环在第一次迭代中运行较慢?

  •  5
  • Bartvbl  · 技术社区  · 8 年前

    #include <iostream>
    #include <chrono>
    
    int main(int argc, char** argv) {
        const long iterationCount = 10000;
        long count = 0;
        for(long i = 0; i < iterationCount; i++) {
            auto start = std::chrono::high_resolution_clock::now();
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            count++;count++;count++;count++;count++;count++;count++;count++;count++;count++;
            auto end = std::chrono::high_resolution_clock::now();
    
            auto timeTaken = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
            std::cout << timeTaken << " " << std::endl;
        }
    }
    

    我使用G++编译了这个,但没有启用编译器优化:

    g++ chrono.cpp -o chrono
    

    我随后运行了几次这个程序,得到了一个有趣的模式。在前500-1000次迭代中,程序的运行速度大约是其余迭代的7-8倍。

    以下是此程序的示例输出: https://pastebin.com/tUQsQEAQ

    如果有关系的话,我的操作系统是ubuntu18.04,g++版本是7.3.0。

    5 回复  |  直到 8 年前
        1
  •  4
  •   Margaret Bloom    8 年前

    在微体系结构实现定义了时间之后,如果CPU可以找到相同的热功率余量,频率缩放就会启动,以将要求较高的内核的时钟加速到最大值(在TDP的限制范围内)。

    英特尔的实现称为 Turbo boost .

    如果在系统中禁用频率缩放(例如 sudo cpupower frequency-set --governor performance cpupower 是在 cpupowerutils


    循环本身是很容易预测的,如果不是只有一个预测失误的话,那么你只能期望有几个预测失误
    所以你可以排除分支预测失误。

    对于I-缓存(或多或少),应用程序几乎不使用D-缓存,除非C++库实现在变量使用上很重。
    代码应该足够小,以适合L1-I,并且大部分代码甚至适合DSB。

    一般来说,从循环携带的依赖链的第二次迭代开始,代码运行得更快是众所周知的,因为CPU第一次填充缓存(数据、指令、TLB)。
    如果CPU耗尽资源,它最终会再次减慢速度,例如,如果存在大量端口压力(例如,大量相同的有限端口长延迟指令),RS可能会在暂停FE时填满,或者如果MOB/SB/LB中填充了大量load/store或BOB中填充了大量jump。
    然而,这些效果很快就会生效,以至于它们控制了代码的运行时间。
    在这种情况下,减速发生得非常晚(在CPU时间内),这就需要考虑按需处理,比如Turbo boost。

        2
  •  1
  •   geza    8 年前

    正如其他人所说,几乎可以肯定的是,您会体验到动态CPU频率缩放的效果。

    我可以在我的机器上复制你的结果。但是,如果我用 cpufreq-set

        3
  •  0
  •   NoSenseEtAl    8 年前

    您所处的范围可能低于系统的分辨率。 考虑一下linux中的这句话 documentation ( 由我负责):

    可选配置通过配置高分辨率定时器。在一个系统上 支持HRTs,睡眠和定时系统调用的准确性是没有的 不再受jiffy约束,而是可以精确到 硬件允许( 硬件

        4
  •  0
  •   mksteve    8 年前

    395
    16592
    395
    395
    395
    790
    395
    790
    395
    395
    395
    790
    

    与你的追踪结果相符。在Windows上,值 395 似乎与时钟计数器锁相,因此时间长度要么是395、790,要么是一个非常大的数字(例如。 116592 ). 真正大的数字看起来像一个上下文切换-我们的程序没有运行。

    你在我的Windows机器上的程序没有最初的减速。

    但是,结果通常与pastebin文件中的结果非常相似。

    所以问题是为什么循环在开始的时候需要更长的时间。我们可以看到这不是一个上下文切换,因为它们看起来明显更长(18k)。因此,处理器在程序开始时的工作速度肯定较慢。造成这种缓慢的原因可能是CPU上的其他内核正在清除我们内核的缓存,或者使用CPU的共享缓存。

    我将通过添加一个初始睡眠来验证这一点,以便系统在启动时稳定下来,只有在机器稳定下来之后才运行循环。

        5
  •  0
  •   BeeOnRope    8 年前

    因此,一种常见的模式是在最初的几毫秒内看到缓慢的时间,之后运行时间会减少(随着CPU的加速),并最终稳定下来。