以纳秒为单位测量精确的时间C ++

时间:2018-08-01 16:32:20

标签: c++ performance profiling clock

我想测试一种在C ++中以十亿分之一秒(可以达到100毫微秒的精度)测量精确代码执行时间的方法。

为此,我尝试使用chrono :: high_resolution_clock。为了测试它是否正常工作。我执行以下操作:

  1. 使用high_resolution_clock获取当前时间(以纳秒为单位),将其称为“开始”
  2. 使用nanosleep(x)睡眠“ x”纳秒
  3. 使用high_resolution_clock获取当前时间(以纳秒为单位),将其称为“结束”
  4. 现在“结束”-“开始”应该与“ x”大致相同。我们将此差异称为“差异”

我对x进行了上述测试,从10到1000000不等。我得到的差异约为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

2 个答案:

答案 0 :(得分:3)

您要尝试做的并不是在所有平台上,甚至在大多数平台上都可以使用。为什么有两个原因。

第一个也是最大的原因是,就其本质而言,测量在内部/内部执行代码的准确时间是不精确的。它需要一个黑匣子的OS调用来确定,如果您一开始就看过这些调用是如何实现的,那么很快就会发现该技术固有的不精确性。在Windows上,这是通过测量处理器的当前“滴答声”及其报告的频率,然后相乘以确定两次连续调用之间经过了多少纳秒来完成的。但是Windows仅以开始时以微秒为单位进行报告,并且即使CPU更改频率(即使仅适度)(这在现代CPU中也很常见,在CPU未被用尽时降低频率以节省功率)才开始报告。会歪曲结果。

Linux也有类似的怪癖,每个操作系统都受CPU准确报告其滴答计数器/滴答速率的能力的支配。

您将获得与所观察到的结果类似的结果的第二个原因是,由于与第一个原因类似的原因,“休眠”线程通常非常不精确。 CPU通常无法以比微秒精度更高的精度来睡眠,并且通常一次睡眠的时间不能超过半毫秒。您的特定环境似乎至少具有几百微秒的精度,但是显然没有比这更精确的了。有些环境甚至会完全降低纳秒分辨率。

总的来说,假设不使用显式实时OS的特定API对该操作系统的特定API进行编程,就可以得到您期望/期望的精度,这可能是一个错误。如果您需要有关各个代码段的时间安排的可靠信息,则需要一遍又一遍地运行所述代码,获取整个执行的样本,然后取平均数,以大致了解每次运行的时间。仍然是不精确的,但是它将帮助您克服这些限制。

答案 1 :(得分:1)

这是nanosleep描述的一部分:

  

如果req中指定的间隔不是时钟基础粒度的精确倍数(请参见time(7)),则该间隔将舍入为下一个整数。此外,在睡眠完成之后,在CPU可以自由再次执行调用线程之前,仍可能会有一个延迟。

您得到的行为似乎与描述非常吻合。

对于极短的停顿,您可能必须自己完成一些(大部分?)工作。系统时钟源的粒度通常为微秒左右。

一种暂停时间少于系统时钟时间的可能方法是测量时钟更改之前执行循环的频率。例如,在启动过程中执行几次该操作,以了解每微秒可以执行多少个循环。

然后暂停一段时间,您可以进行线性插值,多次猜测以执行循环以获得大致相同的暂停时间。

注意:在暂停期间,这通常会使CPU以100%的速度运行,因此,您只想在真正的暂停时执行它-最多一两秒就可以了,但是,如果您想要的远不止于此,您可能想回到纳米睡眠状态。

尽管如此,您仍然需要注意,暂停可能会比计划的时间更长。操作系统会进行时间分片。如果您的进程的时间片在暂停循环的中间到期,那么很容易在计划再次运行之前可能需要数十毫秒(或更长时间)。

如果您确实需要保证此命令的响应时间,则可能需要考虑使用其他操作系统(但即使这不是万灵药,无论您采用何种方式,您都要求的并不是一件容易的事它)。

参考

nanosleep man page