为什么这个延迟循环在没有睡眠的情况下经过几次迭代后开始运行得更快?

2024-10-17 08:46:00
admin
原创
256
摘要:问题描述:考虑:#include <time.h> #include <unistd.h> #include <iostream> using namespace std; const int times = 1000; const int N =...

问题描述:

考虑:

#include <time.h>
#include <unistd.h>
#include <iostream>
using namespace std;

const int times = 1000;
const int N = 100000;

void run() {
  for (int j = 0; j < N; j++) {
  }
}

int main() {
  clock_t main_start = clock();
  for (int i = 0; i < times; i++) {
    clock_t start = clock();
    run();
    cout << "cost: " << (clock() - start) / 1000.0 << " ms." << endl;
    //usleep(1000);
  }
  cout << "total cost: " << (clock() - main_start) / 1000.0 << " ms." << endl;
}

以下是示例代码。在计时循环的前 26 次迭代中,该run函数耗时约为 0.4 毫秒,但随后耗时减少至 0.2 毫秒。

usleep取消注释时,延迟循环在所有运行中花费 0.4 毫秒,从未加速。为什么?

代码编译时g++ -O0未进行优化,因此延迟循环未经过优化。它在 Intel(R) Core(TM) i3-3220 CPU @ 3.30 GHz 上运行,使用 3.13.0-32-generic Ubuntu 14.04.1 LTS (Trusty Tahr)。


解决方案 1:

经过 26 次迭代后,由于您的进程连续几次使用了其完整时间片,因此 Linux 会将 CPU 提升到最大时钟速度。

如果您使用性能计数器而不是挂钟时间进行检查,您会发现每个延迟循环的核心时钟周期保持不变,证实这只是DVFS的效果(所有现代 CPU 在大多数情况下都使用它来以更节能的频率和电压运行)。

如果您在具有内核支持新电源管理模式(硬件完全控制时钟速度)的Skylake上进行测试,则加速将会更快。

如果您让它在具有 Turbo 的 Intel CPU上运行一段时间,一旦热限制要求时钟速度降低回最大持续频率,您可能会看到每次迭代的时间再次略有增加。 (有关 Turbo 让 CPU 运行速度超过其在高功率工作负载下可以维持的速度的更多信息,请参阅为什么我的 CPU 无法在 HPC 中保持峰值性能。)


引入会usleep阻止Linux 的 CPU 频率调节器提高时钟速度,因为即使在最低频率下,该进程也不会产生 100% 的负载。(即内核的启发式方法决定 CPU 的运行速度足以应付其上运行的工作负载。)



对其他理论的评论

回复:David 的理论,即潜在的上下文切换usleep可能会污染缓存:这通常不是一个坏主意,但它无助于解释这段代码。

对于这个实验来说,缓存/TLB 污染根本不重要。除了堆栈末尾之外,计时窗口内基本上没有任何东西会接触内存。大部分时间都花在一个很小的循环(1 行指令缓存)中,该循环仅接触int堆栈内存之一。期间任何潜在的缓存污染都usleep只是此代码时间的一小部分(实际代码会有所不同)!

对于 x86 的更多详细信息:

对其自身的调用clock()可能会发生缓存未命中,但代码提取缓存未命中会延迟开始时间测量,而不是成为测量的一部分。对的第二次调用clock()几乎永远不会被延迟,因为它在缓存中应该仍然很热。

run函数可能位于不同的缓存行中main(因为 gcc 标记main为“冷”,因此它优化较少,并与其他冷函数/数据放在一起)。我们可以预期一到两个指令缓存未命中。不过,它们可能仍在同一个 4k 页中,因此main在进入程序的定时区域之前会触发潜在的 TLB 未命中。

gcc -O0 将会把 OP 的代码编译成类似这样的内容(Godbolt Compiler explorer):将循环计数器保存在堆栈的内存中。

空循环将循环计数器保存在堆栈内存中,因此在典型的Intel x86 CPU上,循环在 OP 的 IvyBridge CPU 上每 ~6 个周期运行一次迭代,这要归功于存储转发延迟,它是add内存目标(读取 - 修改 - 写入)的一部分。 100k iterations * 6 cycles/iteration是 600k 个周期,这主导了最多几个缓存未命中的贡献(每个代码提取未命中约 200 个周期,这会阻止进一步的指令发出,直到它们被解决)。

无序执行和存储转发应该主要隐藏访问堆栈时的潜在缓存未命中(作为call指令的一部分)。

即使循环计数器保存在寄存器中,100k 个周期也是很多的。

解决方案 2:

调用usleep可能会也可能不会导致上下文切换。如果会导致,则所需时间会比不会导致时更长。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用