测量精确的时间,单位为纳秒C++
measuring precise time in nanoseconds C++
我想测试一种在C++中以纳秒为单位测量代码精确执行时间的方法(精确到100纳秒是可以的)。
为此,我尝试使用chrono::high_resolution_clock。为了测试它是否正常工作。我做以下事情:
- 使用high_resolution_clock获取当前时间(以纳秒为单位),称之为"启动">
- 使用nanosleep(x)睡眠"x"纳秒
- 使用high_resolution_clock以纳秒为单位获取当前时间,称之为"结束">
- 现在"end"-"start"应该与"x"大致相同。让我们把这种差异称为"diff">
我对10到1000000之间的x进行了上述测试。我得到的差异大约是100000,即(100微秒)
因为这不应该超过100纳秒。请帮我解决这个问题。
#include <ctime>
#include <unistd.h>
#include <iostream>
#include <chrono>
using namespace std;
int main() {
int sleep_ns[] = {10, 50, 100, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000};
int n = sizeof(sleep_ns)/sizeof(int);
for (int i = 0; i < n; i++) {
auto start = std::chrono::high_resolution_clock::now();
timespec tspec = {0, sleep_ns[i]};
nanosleep(&tspec, NULL);
auto end = std::chrono::high_resolution_clock::now();
chrono::duration<int64_t, nano> dur_ns = (end - start);
int64_t measured_ns = dur_ns.count();
int64_t diff = measured_ns - sleep_ns[i];
cout << "diff: " << diff
<< " sleep_ns: " << sleep_ns[i]
<< " measured_ns: " << measured_ns << endl;
}
return 0;
}
下面是我的机器上这段代码的输出。它运行的"Ubuntu 16.04.4 LTS">
diff: 172747 sleep_ns: 10 measured_ns: 172757
diff: 165078 sleep_ns: 50 measured_ns: 165128
diff: 164669 sleep_ns: 100 measured_ns: 164769
diff: 163855 sleep_ns: 500 measured_ns: 164355
diff: 163647 sleep_ns: 1000 measured_ns: 164647
diff: 162207 sleep_ns: 2000 measured_ns: 164207
diff: 160904 sleep_ns: 5000 measured_ns: 165904
diff: 155709 sleep_ns: 10000 measured_ns: 165709
diff: 145306 sleep_ns: 20000 measured_ns: 165306
diff: 115915 sleep_ns: 50000 measured_ns: 165915
diff: 125983 sleep_ns: 100000 measured_ns: 225983
diff: 115470 sleep_ns: 200000 measured_ns: 315470
diff: 115774 sleep_ns: 500000 measured_ns: 615774
diff: 116473 sleep_ns: 1000000 measured_ns: 1116473
您想要做的并不是在每个平台上,甚至大多数平台上都能工作。原因有几个。
第一个也是最大的原因是,从本质上讲,衡量代码执行的精确时间是不精确的。它需要一个黑盒操作系统调用来确定,如果你一开始就了解过这些调用是如何实现的,那么很快就会发现该技术存在固有的不精确性。在Windows上,这是通过测量处理器的当前"节拍"及其报告的频率来完成的,并将两者相乘以确定两个连续调用之间经过了多少纳秒。但Windows一开始的报告精度仅为微秒,如果CPU改变其频率,即使只是适度地(这在现代CPU中很常见,在CPU未达到最大功率时降低频率,以节省电源),也会扭曲结果。
Linux也有类似的怪癖,每个操作系统都取决于CPU准确报告自己的刻度计数器/刻度率的能力。
第二个原因是,由于与第一个原因类似的原因,"睡眠"线程通常非常不精确。CPU通常无法以比微秒精度更好的精度睡眠,而且一次睡眠速度通常不可能超过半毫秒。您的特定环境似乎至少能够达到几百微秒的精度,但它显然不会比这更精确。有些环境甚至会完全降低纳秒分辨率。
总之,假设在不为明确的实时操作系统编程的情况下,使用该操作系统的特定API,您可以获得您期望/期望的精度,这可能是错误的。如果你想要关于单个代码片段的时间安排的可靠信息,你需要一遍又一遍地运行所述代码,获得整个执行的样本,然后取平均值,以大致了解每次运行的时间安排。它仍然是不精确的,但它将有助于克服这些限制。
以下是纳米睡眠的部分描述:
如果req中指定的间隔不是底层时钟粒度的精确倍数(请参见时间(7)),则该间隔将四舍五入到下一个倍数。此外,在睡眠完成之后,在CPU变得可以再次执行调用线程之前,可能仍然存在延迟。
您得到的行为似乎与描述非常吻合。
对于极短的停顿,你可能不得不自己做一些(大部分?)工作。系统时钟源通常具有微秒左右的粒度
一种可能的暂停时间小于系统时钟时间的方法是测量在时钟改变之前执行循环的频率。在启动过程中(例如)这样做几次,以很好地了解每微秒可以执行多少个循环。
然后,要暂停一小部分时间,可以进行线性插值,以猜测执行循环的次数,从而获得大致相同的暂停长度。
注意:这通常会在暂停期间以100%的速度运行CPU,所以你只想在真正短暂的暂停时这样做——最多一两微秒是可以的,但如果你想要更多,你可能想回到nanosleep。
然而,即便如此,你也需要意识到,暂停可能会比你计划的时间长得多。操作系统进行时间切片。如果进程的时间片在暂停循环的中间过期,那么在计划再次运行之前,很容易需要数十毫秒(或更长时间)。
如果你真的需要保证这个订单的响应时间,你可能需要考虑另一个操作系统(但即使这样也不是万能的——无论你如何处理,你所要求的都不是微不足道的)。
参考
nanosleep手册页
- 芬威克树(BIT).找到具有给定累积频率的最小索引,单位为 O(logN)
- RcppEigen 模板化函数,用于填充单位法线
- 如何有效地计算将单位立方体映射到自身的反射和旋转?
- 以天C++为单位的两个时间戳之间的差异
- 如何以毫秒为单位获取开始时间和 now() 之间的毫秒差异(以 C++为单位?
- 从原始字节解码协议缓冲区(以 C++为单位)
- ANTLR - 如何从维度扩展单位
- 如何在 c++ 中确定一条指令(以字节为单位)在哪里结束,另一条指令从哪里开始?
- 编写以 C++ 为单位返回值的函数
- 纹理单位重叠?渲染了错误的纹理
- Cuda:具有位集数组的 XOR 单位集
- 以C++为单位进行运行长度编码
- 如何找到两个日期之间的时间差异(以秒和纳秒为单位)?
- arr[n] 是否以 C++ 为单位打印数组的长度?
- 字符串数组上的 sizeof 运算符以 C++ 为单位给出不同的输出
- 以 C++ 为单位具有输出限制的排列
- 以 GDB 为单位指定浮点精度
- 整数数据如何以位为单位存储在内存中?不是右对齐吗?
- 如何在没有硬编码的情况下以C++为单位获取类数组的长度?
- 为什么在函数中 'int main()' -> 's' 可以在此函数中使用单位化?