use*_*660 2 c++ resolution timing
我正在尝试测量并行端口上 2 个信号之间的时间差,但首先我要知道我的测量系统(AMD Athlon(tm) 64 X2 双核处理器 5200+ \xc3\x97 2) 在 SUSE 12.1 x64 上。
\n\n因此,经过一番阅读后,我决定使用clock_gettime(),首先我使用以下代码获取clock_getres()值:
\n\n/*\n * This program prints out the clock resolution.\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n\nint main( void )\n {\n struct timespec res;\n\n if ( clock_getres( CLOCK_REALTIME, &res) == -1 ) {\n perror( "clock get resolution" );\n return EXIT_FAILURE;\n }\n printf( "Resolution is %ld nano seconds.\\n",\n res.tv_nsec);\n return EXIT_SUCCESS;\n }\nRun Code Online (Sandbox Code Playgroud)\n\n结果是:1 纳秒。我很高兴!
\n\n但这是我的问题,当我尝试用其他代码检查这一事实时:
\n\n#include <iostream>\n#include <time.h>\nusing namespace std;\n\ntimespec diff(timespec start, timespec end);\n\nint main()\n{\n timespec time1, time2, time3,time4;\n int temp;\n time3.tv_sec=0;\n time4.tv_nsec=000000001L;\n clock_gettime(CLOCK_REALTIME, &time1);\n NULL;\n clock_gettime(CLOCK_REALTIME, &time2);\n cout<<diff(time1,time2).tv_sec<<":"<<diff(time1,time2).tv_nsec<<endl;\n return 0;\n}\n\ntimespec diff(timespec start, timespec end)\n{\n timespec temp;\n if ((end.tv_nsec-start.tv_nsec)<0) {\n temp.tv_sec = end.tv_sec-start.tv_sec-1;\n temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;\n } else {\n temp.tv_sec = end.tv_sec-start.tv_sec;\n temp.tv_nsec = end.tv_nsec-start.tv_nsec;\n }\n return temp;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这个计算了两次调用clock_gettime之间的时间,time3和time4被声明但在这个例子中没有使用,因为我正在用它们做测试。
\n\n本例中的输出在 978 到 1467 ns 之间波动。这两个数字都是 489 的倍数,这让我认为 489 ns 是我真正的分辨率。与上面获得的 1 ns 相差甚远。
\n\n我的问题:有什么方法可以获得更好的结果吗?我错过了什么吗?
\n\n我的项目确实需要至少 10 纳秒的分辨率。快点!GPS 可以获得比 PC 更好的分辨率?
\n小智 6
我意识到这个话题早已消亡,但想提出我的发现。这是一个很长的答案,所以我把简短的答案放在这里,有耐心的人可以费力地完成其余的部分。这个问题的不完全答案是 700 ns 或 1500 ns,具体取决于您使用的clock_gettime() 模式。长答案要复杂得多。
\n作为参考,我做这项工作的机器是一台没人想要的旧笔记本电脑。它是运行 Ubuntu 14.041 LTS 的 Acer Aspire 5720Z。
\n硬件:
\nRAM:2.0 GiB // 这是 Ubuntu 在“系统设置”\xe2\x86\x92“详细信息”中的报告方式
\n处理器:Intel\xc2\xae Pentium(R) Dual CPU T2330 @ 1.60GHz \xc3\x97 2
\n显卡:Intel\xc2\xae 965GM x86/MMX/SSE2
我想在即将到来的项目中准确地测量时间,并且作为 PC 硬件的相对新手,无论操作系统如何,我想我应该对计时硬件的分辨率进行一些实验。我偶然发现了这个问题。
\n由于这个问题,我认为clock_gettime()看起来满足我的需求。但我过去使用 PC 硬件的经验让我不知所措,因此我开始进行一些实验,看看计时器的实际分辨率是多少。
\n方法:从clock_gettime() 收集结果的连续样本并查看分辨率中的任何模式。代码如下。
\n结果摘要稍长:
在继续之前,我最好定义模式:哪个模式是哪个?:
\nMode 0 CLOCK_REALTIME // 参考:http://linux.die.net/man/3/clock_gettime
\nMode 1 CLOCK_MONOTONIC
\nMode 2 CLOCK_PROCESS_CPUTIME_ID
\nMode 3 CLOCK_THREAD_CPUTIME_ID
结论:对我来说,如果分辨率小于函数获取时间间隔所需的时间长度,那么谈论时间间隔的分辨率是没有意义的。例如,如果我们使用模式 3,我们知道该函数 99% 的时间都会在 700 纳秒内完成。而且我们还知道,我们返回的时间间隔将是 7 纳秒的倍数。因此,7 纳秒的“分辨率”是调用获取时间的时间的 1/100。我没有看到 7 纳秒的变化间隔有任何价值。对于分辨率问题有 3 种不同的答案:1 ns、7 或 70 ns,最后是 700 或 1500 ns。我赞成最后一个数字。
\n总而言之,如果您想测量某些操作的性能,您需要记住clock_gettime()调用需要\xe2\x80\x93多长时间,即700或1500 ns。例如,尝试测量需要 7 纳秒的东西是没有意义的。为了便于讨论,假设您愿意接受性能测试结论中 1% 的错误。如果使用模式 3(我想我将在我的项目中使用),您将不得不说您需要测量的间隔需要是 700 纳秒或 70 微秒的 100 倍。否则你的结论会有超过1%的误差。因此,继续测量您感兴趣的代码,但如果您在感兴趣的代码中运行的时间少于 70 微秒,那么您最好循环遍历感兴趣的代码足够多次,以便间隔更像 70 微秒或更多。
\n这些主张的理由和一些细节:
\n先索赔3。这很简单。只需大量运行clock_gettime()并将结果记录在数组中,然后处理结果即可。在循环外进行处理,以使clock_gettime() 调用之间的时间尽可能短。
\n这一切意味着什么?参见附图。例如,对于模式 0,大多数情况下对clock_gettime() 的调用花费的时间不到 1.5 微秒。可以看到模式0和模式1基本是一样的。然而,模式 2 和 3 与模式 0 和 1 有很大不同,并且彼此之间也略有不同。与模式 0 和 1 相比,模式 2 和 3 花费的 Clock_gettime() 挂钟时间大约是模式 0 和 1 的一半。另请注意,模式 0 和 1 与模式 2 和 3 不同,\xe2\x80\x93 彼此略有不同。模式 0 和模式 1 相差 70 纳秒 \xe2\x80\x93,这是一个我们将在权利要求 2 中讨论的数字。
\n所附图表的范围限制为 2 微秒。否则,数据中的异常值会阻止图表传达前一点。该图没有说明的是,模式 0 和 1 的异常值比模式 2 和 3 的异常值要差得多。换句话说,不仅是平均值,而且是统计“模式”(所有这些模式的中值(即第 50 个百分位数)都不同,因此是否存在最大值及其第 99 个百分位数。
\n所附图表针对四种模式中每种模式的 100,001 个样本。请注意,绘制图表的测试仅使用处理器 0 的 CPU 掩码。无论我是否使用 CPU 亲和力似乎对图表没有任何影响。
\n声明 2:如果您仔细观察准备图表时收集的样本,您很快就会注意到差异之间的差异(即二阶差异)在 70 纳秒左右(在模式 0 和模式之前)相对恒定 \xe2\x80\x93至少 1 个)。要重复此实验,请像以前一样收集“n”个时钟时间样本。然后计算每个样本之间的差异。现在将差异按顺序排序(例如 sort -g),然后导出各个独特的差异(例如 uniq -c)。
\n例如:
\n$ ./Exp03 -l 1001 -m 0 -k | sort -g | awk -f mergeTime2.awk | awk -f percentages.awk | sort -g\n1.118e-06 8 8 0.8 0.8 // time,count,cumulative count, count%, cumulative count%\n1.188e-06 17 25 1.7 2.5\n1.257e-06 9 34 0.9 3.4\n1.327e-06 570 604 57 60.4\n1.397e-06 301 905 30.1 90.5\n1.467e-06 53 958 5.3 95.8\n1.537e-06 26 984 2.6 98.4\n<snip>\nRun Code Online (Sandbox Code Playgroud)\n第一列中的持续时间之间的差异通常是 7e-8 或 70 纳秒。通过处理差异可以使这一点变得更加清晰:
\n$ <as above> | awk -f differences.awk \n7e-08\n6.9e-08\n7e-08\n7e-08\n7e-08\n7e-08\n6.9e-08\n7e-08\n2.1e-07 // 3 lots of 7e-08\n<snip>\nRun Code Online (Sandbox Code Playgroud)\n请注意所有差异都是 70 纳秒的整数倍吗?或者至少在 70 纳秒的舍入误差内。
\n这个结果很可能取决于硬件,但我实际上不知道目前是什么将其限制为 70 纳秒。也许某处有 14.28 MHz 振荡器?
\n请注意,在实践中我使用了更多的样本,例如 100,000 个,而不是上面的 1000 个。
\n相关代码(附后):
\n\'Expo03\' 是尽可能快地调用clock_gettime() 的程序。请注意,典型用法如下:
\n./Expo03 -l 100001 -m 3
\n这将调用clock_gettime() 100,001次,以便我们可以计算100,000个差异。本例中对clock_gettime()的每次调用都将使用模式3。
\nMergeTime2.awk 是一个有用的命令,它是一个美化的“uniq”命令。问题是,二阶差异通常是 69 和 1 纳秒成对的,而不是 70 纳秒(至少对于模式 0 和 1),正如我到目前为止让您相信的那样。因为不存在 68 纳秒差异或 2 纳秒差异,所以我将这 69 纳秒和 1 纳秒对合并为一组 70 纳秒。为什么会出现 69/1 的行为很有趣,但将它们视为两个单独的数字通常会给分析增加“噪音”。
\n在你问之前,我已经重复了这个避免浮点的练习,但同样的问题仍然发生。所得的 tv_nsec 作为整数具有 69/1 行为(或 1/7 和 1/6),因此请不要假设这是由浮点减法引起的假象。
\n请注意,我对 70 ns 和 70 ns 的小整数倍的这种“简化”充满信心,但对于 7 ns 的情况,这种方法看起来不太稳健,尤其是当您获得 7 ns 分辨率的 10 倍的二阶差异时。
\n案例中附有percentages.awk 和differences.awk。
\n停止媒体:我无法发布图表,因为我没有“至少 10 的声誉”。对此感到抱歉。
\n罗布·沃森\n2014 年 11 月 21 日
\n世博03.cpp
\n/* Like Exp02.cpp except that here I am experimenting with\n modes other than CLOCK_REALTIME\n RW 20 Nov 2014\n*/\n\n/* Added CPU affinity to see if that had any bearing on the results\n RW 21 Nov 2014\n*/\n\n#include <iostream>\nusing namespace std;\n#include <iomanip>\n\n#include <stdlib.h> // getopts needs both of these\n#include <unistd.h>\n\n#include <errno.h> // errno\n\n#include <string.h> // strerror()\n\n#include <assert.h>\n\n// #define MODE CLOCK_REALTIME\n// #define MODE CLOCK_MONOTONIC\n// #define MODE CLOCK_PROCESS_CPUTIME_ID\n// #define MODE CLOCK_THREAD_CPUTIME_ID\n\nint main(int argc, char ** argv)\n{\n int NumberOf = 1000;\n int Mode = 0;\n int Verbose = 0;\n int c;\n // l loops, m mode, h help, v verbose, k masK\n\n\n int rc;\n cpu_set_t mask;\n int doMaskOperation = 0;\n\n while ((c = getopt (argc, argv, "l:m:hkv")) != -1)\n {\n switch (c)\n {\n case \'l\': // ell not one\n NumberOf = atoi(optarg);\n break;\n case \'m\':\n Mode = atoi(optarg);\n break;\n case \'h\':\n cout << "Usage: <command> -l <int> -m <mode>" << endl\n << "where -l represents the number of loops and "\n << "-m represents the mode 0..3 inclusive" << endl\n << "0 is CLOCK_REALTIME" << endl\n << "1 CLOCK_MONOTONIC" << endl\n << "2 CLOCK_PROCESS_CPUTIME_ID" << endl\n << "3 CLOCK_THREAD_CPUTIME_ID" << endl;\n break;\n case \'v\':\n Verbose = 1;\n break;\n case \'k\': // masK - sorry! Already using \'m\'...\n doMaskOperation = 1;\n break;\n case \'?\':\n cerr << "XXX unimplemented! Sorry..." << endl;\n break;\n default:\n abort();\n }\n }\n\n if (doMaskOperation)\n {\n if (Verbose)\n {\n cout << "Setting CPU mask to CPU 0 only!" << endl;\n }\n CPU_ZERO(&mask);\n CPU_SET(0,&mask);\n assert((rc = sched_setaffinity(0,sizeof(mask),&mask))==0);\n }\n\n if (Verbose) {\n cout << "Verbose: Mode in use: " << Mode << endl;\n }\n\n if (Verbose)\n {\n rc = sched_getaffinity(0,sizeof(mask),&mask);\n // cout << "getaffinity rc is " << rc << endl;\n // cout << "getaffinity mask is " << mask << endl;\n int numOfCPUs = CPU_COUNT(&mask);\n cout << "Number of CPU\'s is " << numOfCPUs << endl;\n for (int i=0;i<sizeof(mask);++i) // sizeof(mask) is 128 RW 21 Nov 2014\n {\n if (CPU_ISSET(i,&mask))\n {\n cout << "CPU " << i << " is set" << endl;\n }\n //cout << "CPU " << i \n // << " is " << (CPU_ISSET(i,&mask) ? "set " : "not set ") << endl;\n }\n }\n\n clockid_t cpuClockID;\n int err = clock_getcpuclockid(0,&cpuClockID);\n if (Verbose)\n {\n cout << "Verbose: clock_getcpuclockid(0) returned err " << err << endl;\n cout << "Verbose: clock_getcpuclockid(0) returned cpuClockID " \n << cpuClockID << endl;\n }\n\n timespec timeNumber[NumberOf];\n for (int i=0;i<NumberOf;++i)\n {\n err = clock_gettime(Mode, &timeNumber[i]);\n if (err != 0) {\n int errSave = errno;\n cerr << "errno is " << errSave \n << " NumberOf is " << NumberOf << endl;\n cerr << strerror(errSave) << endl;\n cerr << "Aborting due to this error" << endl;\n abort();\n }\n }\n\n for (int i=0;i<NumberOf-1;++i)\n {\n cout << timeNumber[i+1].tv_sec - timeNumber[i].tv_sec\n + (timeNumber[i+1].tv_nsec - timeNumber[i].tv_nsec) / 1000000000.\n << endl;\n \n }\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n合并时间2.awk
\nBEGIN {\n PROCINFO["sorted_in"] = "@ind_num_asc"\n}\n\n{array[$0]++}\n\nEND {\n lastX = -1;\n first = 1;\n \n for (x in array)\n {\n if (first) { \n first = 0 \n lastX = x; lastCount = array[x]; \n } else {\n delta = x - lastX;\n if (delta < 2e-9) { # this is nasty floating point stuff!!\n lastCount += array[x]; \n lastX = x\n } else {\n Cumulative += lastCount;\n print lastX "\\t" lastCount "\\t" Cumulative\n lastX = x; \n lastCount = array[x]; \n }\n }\n }\n print lastX "\\t" lastCount "\\t" Cumulative+lastCount\n}\nRun Code Online (Sandbox Code Playgroud)\n百分比.awk
\n{ # input is $1 a time interval $2 an observed frequency (i.e. count)\n # $3 is a cumulative frequency\n b[$1]=$2;\n c[$1]=$3;\n sum=sum+$2\n} \n\nEND {\n for (i in b) print i,b[i],c[i],(b[i]/sum)*100, (c[i]*100/sum);\n}\nRun Code Online (Sandbox Code Playgroud)\n差异.awk
\nNR==1 {\n old=$1;next\n} \n{\n print $1-old;\n old=$1\n}\nRun Code Online (Sandbox Code Playgroud)\n