我编写了一些测试代码,使用__rdtscp来重复调用clock_gettime(rdtscp调用绕过一个调用clock_gettime的循环并将结果添加到一起,这样编译器就不会进行太多的优化).
如果我快速连续调用clock_gettime(),则时间长度从大约45k时钟周期下降到500个周期.其中一些我认为可能是第一次调用必须加载vDSO代码(仍然没有完全对我有意义),但如何需要一些调用来获得500我根本无法解释,这种行为似乎无论我如何测试它都是恒定的:
4246711141077496455
但是,如果我在调用clock_gettime之间休眠(一秒或十分,无关紧要),它只会达到约4.7k周期的稳定状态:
这里睡10秒钟:
282931093472947564736
这里睡1秒钟:
6157885547534741564547534732
缓存行为似乎无法描述这一点(在桌面系统上没有做太多任何事情).我应该为clock_gettime的调用预算多少钱?为什么呼叫变得越来越快?为什么睡一小段时间这么重要?
tl; dr我试图理解调用clock_gettime所花费的时间(CLOCK_REALTIME,…)不明白为什么它在快速连续调用时运行得更快而不是在调用之间的第二次调用.
更新:这是proc 0上的cpuinfo
processor : 0vendor_ID : GenuineIntelcpu family : 6model : 158model name : Intel(R) Core(TM) i7-7700HQ cpu @ 2.80GHzstepPing : 9microcode : 0x84cpu MHz : 2800.000cache size : 6144 KBphysical ID : 0siblings : 8core ID : 0cpu cores : 4APIcID : 0initial APIcID : 0fpu : yesfpu_exception : yescpuID level : 22wp : yesflags : fpu vme de pse tsc msr pae mce cx8 APIc sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuID aperfmperf tsc_kNown_freq pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcID sse4_1 sse4_2 x2APIc movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dNowprefetch cpuID_fault epb intel_pt tpr_shadow vnmi flexpriority ept vpID fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcID mpx rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves dtherm IDa arat pln pts hwp hwp_notify hwp_act_window hwp_eppBUGs :bogomips : 5616.00clflush size : 64cache_alignment : 64address sizes : 39 bits physical,48 bits virtualpower management:
这是重新创建的测试代码:
#include <time.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <x86intrin.h>// compiled gcc -Wall -O3 -o clockt clockt.cpp// called glockt sleeptime trials loopsunsigned long long Now() { struct timespec s; clock_gettime(CLOCK_REALTIME,&s); return (s.tv_sec * 1000000000ull) + s.tv_nsec;}int main(int argc,char **argv) { int sleeptime = atoi(argv[1]); int trials = atoi(argv[2]); int loops = atoi(argv[3]); unsigned long long x,y,n = 0; unsigned int d; x = __rdtscp(&d); n = Now(); asm volatile("": "+r" (n)); y = __rdtscp(&d); printf("init run %lld\n",(y-x)); for(int t = 0; t < trials; ++t) { if(sleeptime > 0) sleep(sleeptime); x = __rdtscp(&d); for(int l = 0; l < loops; ++l) { n = Now(); asm volatile("": "+r" (n)); } y = __rdtscp(&d); printf("trial %d took %lld\n",t,(y-x)); } exit(0);}解决方法 第一次调用clock_gettime时,页面上会发生页面错误,其中包含该函数的指令.在我的系统上,这是一个软页面错误,需要几千个周期来处理(最多10,000个周期).我的cpu运行在3.4GHz.我认为您的cpu运行频率要低得多,因此在系统上处理页面错误会花费更多时间.但这里的重点是,对clock_gettime的第一次调用将比后来的调用花费更多的时间,这正是你所观察到的.
代码显示的第二个主要影响是由于指令缓存未命中而导致的重大停顿.您可能看起来只调用两个函数,即Now和printf,但这些函数调用其他函数,它们都在L1指令缓存上竞争.总的来说,它取决于所有这些功能在物理地址空间中的对齐方式.当休眠时间为零秒时,由于指令高速缓存未命中而导致的停止时间实际上相对较小(您可以使用ICACHE.IFETCH_STALL性能计数器来测量它).但是,当睡眠时间大于零秒时,此停顿时间变得非常大,因为OS将调度一些其他线程在同一核心上运行,并且该线程会有不同的指令和数据.这解释了为什么当你睡觉时,clock_gettime需要更多的时间来执行.
现在关于第二次和以后的测量.从问题:
4246711141077496455
我在我的系统上观察到第二次测量不一定比后来的测量更大.我相信你的系统也是如此.事实上,当您睡眠10秒或1秒时,情况似乎就是这样.在外部循环中,两个函数现在和printf包含数千个动态指令,它们也访问L1数据高速缓存.您在第二次和后续测量之间看到的可变性是可重现的.所以它是功能本身所固有的.请注意,rdtscp指令本身的执行时间可能会有4个周期.另见this.
实际上,当期望的精度最多为一百万个周期时,clock_gettime很有用.否则,它可能会产生误导.
总结以上是内存溢出为你收集整理的linux – 为什么clock_gettime(CLOCK_REALTIME,..)的调用延迟变化如此之大?全部内容,希望文章能够帮你解决linux – 为什么clock_gettime(CLOCK_REALTIME,..)的调用延迟变化如此之大?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)