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

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

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1124  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   79  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   70  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   82  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   74  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用