Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/28.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么这个延迟循环在几次迭代后没有睡眠的情况下开始运行得更快?_C++_Linux_Performance_Benchmarking - Fatal编程技术网

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

C++ 为什么这个延迟循环在几次迭代后没有睡眠的情况下开始运行得更快?,c++,linux,performance,benchmarking,C++,Linux,Performance,Benchmarking,考虑: #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();

考虑:

#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;
}
#包括
#包括
#包括
使用名称空间std;
常数整数倍=1000;
常数N=100000;
无效运行(){
对于(int j=0;jcout对
usleep
的调用可能会导致也可能不会导致上下文切换。如果这样做,则所需时间将比不这样做所需的时间长。

经过26次迭代后,Linux会将CPU提升到最大时钟速度,因为您的进程会连续几次使用其全部时间

如果你用性能计数器而不是墙上的时钟时间来检查,你会发现每个延迟环路的核心时钟周期保持不变,这证实了这只是一个效应(所有现代CPU在大多数时间都以更节能的频率和电压运行)

如果您在一个支持内核的平台上进行测试,那么提升速度会快得多

如果让它运行一段时间,当热限制要求时钟速度降低到最大持续频率时,您可能会看到每次迭代的时间再次略微增加。(有关Turbo让CPU运行速度快于其在高功率工作负载下所能承受的速度的更多信息,请参阅。)


引入
usleep
可以防止时钟速度的提升,因为即使在最低频率下,进程也不会产生100%的负载。(即内核的启发式算法决定CPU的运行速度足以满足其上运行的负载。)



对其他理论的评论

re::一般来说,这不是一个坏主意,但它无助于解释这段代码

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

有关x86的更多详细信息:

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

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

gcc-O0将把OP的代码编译为:将循环计数器保存在堆栈的内存中

空循环将循环计数器保留在堆栈内存中,因此在典型情况下,循环在OP的IvyBridge CPU上每6个周期运行一次,这要归功于存储转发延迟,该延迟是
add
的一部分,带有内存目标(读-修改-写).
100k次迭代*6次循环/次迭代
为600k次循环,这最多会导致几次缓存未命中(每次代码提取未命中约200次循环,这会阻止进一步的指令发出,直到它们得到解决)

无序执行和存储转发应该主要隐藏访问堆栈时可能出现的缓存未命中(作为
调用
指令的一部分)


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

您可能应该检查
usleep()的结果
因为您的参数无效,它可能会被中断,或者可能不起作用。这会使任何计时不可靠。@John3136:usleep在计时窗口之外。他正在对一个繁忙的循环进行计时,或者是背对背计时,或者是以1ms睡眠间隔计时。出于基准测试的目的,您应该使用
gcc-O2
或(因为你的代码是C++)
g++-O2
至少。如果你睡眠1000微秒,我希望循环至少需要1毫秒。你是如何测量0.4毫秒的?@AdrianMcCarthy:
usleep
usleep
自动放弃CPU,因此应该有一个上下文切换(即使系统空闲),不是吗?@rakib如果没有可切换的上下文,或者时间间隔太短,则不会。当您谈论的时间小于10毫秒左右时,操作系统可能会决定不进行上下文切换。@rakib:可以切换到内核模式并返回到内核模式。在恢复调用
us的进程之前,可能不会切换到其他进程leep
,所以缓存/TLB/分支预测器的污染可能最小。@rakib然后
调度
计算下一件事情需要多长时间,然后决定如何等待,可能调度其他事情,可能使用硬件计时器,可能不。@rakib:如果CPU在返回到作为
usleep
的调用者,有些人会说没有上下文切换,即使硬件进入睡眠状态(使用
hlt
指令)很短时间。在这种情况下,缓存/TLB污染肯定最小,IIRC没有TLB失效。(我完全忘记了内核模式的页表是如何工作的,但我不认为每次系统调用都会破坏整个TLB)。我将
N
的值增加了100倍,并使用
cpufreq info
命令,我发现当代码运行时,cpu仍以最低频率工作。@phyxnj:with
usleep
未注释?它为我增加了N=10000000。(我使用
grep-MHz/proc/cpuinfo
,因为我从未抽出时间安装cpuf)