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

2024-10-17 08:46:00
admin
原创
73
摘要:问题描述:考虑:#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可能会也可能不会导致上下文切换。如果会导致,则所需时间会比不会导致时更长。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用