您能否告诉我为什么以下程序打印的timediff
的值通常为4微秒(不同运行的范围为90到1000倍),但有些情况下有时为70或更多微秒(对于不同的运行,在2到10倍的范围内):
#include <iostream>
using namespace std;
#include<sys/time.h>
#define MAXQ 1000000
#define THRDS 3
double GetMicroSecond()
{
timeval tv;
gettimeofday (&tv, NULL);
return (double) (((double)tv.tv_sec * 1000000) + (double)tv.tv_usec);
}
int main()
{
double timew, timer, timediff;
bool flagarray[MAXQ];
int x=0, y=0;
for(int i=0; i<MAXQ; ++i)
flagarray[i] = false;
while(y <MAXQ)
{
x++;
if(x%1000 == 0)
{
timew = GetMicroSecond();
flagarray[y++]=true;
timer = GetMicroSecond();
timediff = timer - timew;
if(timediff > THRDS) cout << timer-timew << endl;
}
}
}
使用编译:g ++ testlatency.cpp -o testlatency
注意:在我的系统中有12个核心。仅使用系统中运行的此程序检查性能。
答案 0 :(得分:0)
通常,有许多线程共享少量核心。除非您采取措施确保您的线程不间断地使用核心,否则您无法保证操作系统不会决定在两次呼叫GetMicroSecond()
呼叫之间抢占您的线程,并让一些其他线程使用核心。
即使您的代码不间断运行,您尝试计时的行也是:
flagarray[y++]=true;
执行的时间可能比测量代码本身少得多。
答案 1 :(得分:0)
在您的程序执行的同时,现代操作系统内部发生了许多事情。他们中的一些人可能会从你的程序中“窃取”CPU,正如NPE的答案所述。还有一些可以影响时间安排的例子:
这些都不容易预测。
如果您在某些微控制器上运行代码,或者使用real time OS,则可以保持一致性。
答案 2 :(得分:0)
timew = GetMicroSecond();
flagarray [Y ++] = TRUE;
timer = GetMicroSecond();
语句flagarray[y++]=true;
在现代计算机上执行将花费不到一微秒如果 flagarray[y++
恰好位于1级缓存中。如果该位置在第2级高速缓存中但不在第1级高速缓存中,则该语句将花费更长的时间,如果它在3级高速缓存中但不在1级或2级高速缓存中则更长,并且如果不是1和2级则更长#39; t在任何缓存中。
使timer-timew
超过3毫秒的另一件事是当你的程序产生OS时。缓存未命中可导致产量。系统调用也是如此。函数gettimeofday
是系统调用。作为一般规则,您应该期望任何系统调用都能产生。
注意:在我的系统中有12个核心。仅使用系统中运行的此程序检查性能。
事实并非如此。总有许多其他程序,以及12核计算机上运行的许多其他线程。这些包括操作系统本身(其中包含许多线程),以及许多小守护进程。每当你的程序产生时,操作系统就可以决定暂时暂停你的程序,以便其中一个临时暂停但却要求使用CPU的其他线程。
其中一个守护进程是网络时间协议守护进程(ntpd)。这为你的系统时钟做了各种时髦的小事,使它与原子钟保持接近。在flagarray[y++]=true
的连续调用之间只有gettimeofday
之类的微小指令,您甚至可能偶尔会看到时间倒退。
在测试时序时,最好在粗略的时间进行时序测量。不要计算不涉及任何函数调用的单个语句。定时循环比定时更好,而不是单独执行循环体的时间。即便如此,由于缓存未命中以及操作系统暂时中止程序的执行,您应该期望时序存在一些差异。
现代基于Unix的系统有更好的计时器(例如,clock_gettime
)而不是gettimeofday
,它们不受网络时间协议守护程序所做的更改。您应该使用其中一个而不是gettimeofday
。
答案 3 :(得分:0)
有很多变量可以解释看到的不同时间值。我会更专注于
中断
bool flagarray [MAXQ];
由于您将MAXQ定义为1000000,因此我们假设flagarray
占用1MB空间。
您可以根据L1 / L2 D-cache大小计算可能发生的缓存未命中数。然后,您可以关联填充所有L1并开始丢失所需的迭代次数以及与L2相同的迭代次数。操作系统可能会计划您的流程并重新安排它 - 但是,我希望由于您拥有的核心数量不太可能。中断的情况也是如此。空闲系统永远不会完全空闲。您可以选择将您的流程与核心编号相关联,例如通过执行
来表示 taskset 0x<MASK> ./exe
并控制其执行。
如果你真的很好奇,我建议你使用&#34; perf&#34;大多数Linux发行版都提供了工具。
你可以做
perf stat -e L1-dcache-loadmisses
或
perf stat -e LLC-load-misses
一旦你有了这些数字和迭代次数,你就会开始构建一个引起注意滞后的活动的图片。您还可以使用&#34; perf stat&#34;。
监控OS调度程序事件