【问题标题】:Time-stamping using std::chrono - How to 'filter' data based on relative time?使用 std::chrono 的时间戳 - 如何根据相对时间“过滤”数据?
【发布时间】:2020-09-15 10:26:31
【问题描述】:

我想对我产生的数据流进行时间标记,我想为此使用 std::chrono::steady_clock

这些时间戳与数据一起存储(作为 uint64 值的数组?),我稍后需要再次处理这些时间戳。

现在,到目前为止,我还没有使用过 std::chrono 库,所以我确实需要一些关于这个库的语法和最佳实践方面的帮助。

我可以使用以下方法获取和存储值:

uint64_t timestamp = std::chrono::steady_clock::now().time_since_epoch().count();

但我如何做到最好:

  • 在读取数据时,从 uint64 创建一个时间点?

  • 获取 stable_clock 的每秒滴答数 (uint64) 值?

  • 找到一个位于给定时间点之前某个时间(以秒为单位)的“截止”时间点(如 uint64)?

以上代码 sn-ps 将不胜感激。

我想结合以上三个本质上执行以下操作:拥有一个(增加的)时间戳值数组(如 uint64),我想截断它,以使所有数据都比上次时间戳“旧”减去 X 秒。

【问题讨论】:

    标签: c++ time chrono


    【解决方案1】:

    让我们看看您可能在chrono 的 cppreference 文档中使用的功能。

    首先,您需要决定要使用哪个时钟。有你建议的steady_clockhigh_resolution_clocksystem_clock

    high_resolution_clock 依赖于实现,所以除非我们真的需要它,否则让我们把它收起来。 steady_clock 保证是单调的,但不能保证您获得的值的含义。它非常适合对事件进行排序或测量其间隔,但您无法从中获取时间点。

    另一方面,system_clock 有一个含义,它是 UNIX 纪元,因此您可以从中获取时间值,但不能保证是单调的。

    要获得steady_clock 的周期(一个滴答的持续时间),您需要period 成员:

    auto period = std::chrono::steady_clock::period();
    std::cout << "Clock period " << period.num << " / " << period.den << " seconds" << std::endl;
    std::cout << "Clock period " << static_cast<double>(period.num) / period.den << " seconds" << std::endl;
    

    假设您想使用steady_clock 值过滤最近几秒钟内发生的事件,您首先需要计算所需时间段内的滴答数并将其从now 中减去。大致如下:

    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    std::time_t t_c = std::chrono::system_clock::to_time_t(now - std::chrono::seconds(10));
    

    并使用t_c 作为分界点。

    但是,不要依赖 std::chrono::steady_clock::now().time_since_epoch().count(); 来获得有意义的东西 - 只是一个数字。 steady_clock 的时期通常是启动时间。如果你需要时间,你应该使用system_clock(记住这不是单调的)。

    C++20a 引入了更多时钟,可以转换为时间。

    【讨论】:

    • 谢谢,这很有帮助,我会研究一下。我知道时钟之间的差异,并希望将 stable_clock 用于其属性。在您的最后一个示例代码行中,我可以直接从时间点减去 uint64_t 吗?另外,如果我想从给定的 uint64 值创建一个 std::chrono::steady_clock::time_point ,我该怎么做(语法)?
    • 我应该补充一点,我的实际任务与此 question & answer 非常相关时间,如有必要。
    • 你对那个减法是正确的 - 我用一个更好(并且输入安全)的版本编辑了我的答案。 std::time_t 只是一个整数类型。
    • 谢谢。不过,我仍在为 chrono 库苦苦挣扎。我根本找不到足够完整的示例代码来帮助我克服障碍......特别是“如果我只有一个 uint64 数字(从 count() 返回的那个),我如何回到一个时间点。)
    • 我认为这不是一个好主意,整个库都旨在从时钟中获取时间点。工作流程应该是使用带有时间点的时钟,对它们进行最终计算,将它们转换为std::time_tuint64_t 并使用它们。如果您需要时钟中的其他任何东西,请再次通过uint64_t 获取。确保在每一点都知道该值代表什么 - 纪元和单位是什么。
    【解决方案2】:

    由于我今天花了很长时间才从各种来源弄清楚,我将在此处发布我的解决方案作为自我回答。 (如果有什么不正确或可以做得更好,我会很感激 cmets。)

    以秒为单位获取时钟周期和每秒滴答数

        using namespace std::chrono;
        auto period = system_clock::period();
        double period_s = (double) period.num / period.den; 
        uint64 tps = period.den / period.num;
    

    将时钟的时间点(现在)作为 uint64 值获取数据流的时间戳

        using namespace std::chrono;
        system_clock::time_point tp_now = system_clock::now();
        uint64 nowAsTicks = tp_now.time_since_epoch().count();
    

    根据存储的 uint64 值获取时钟的时间点

        using namespace std::chrono;
        uint64 givenTicks = 12345;  // Whatever the value was
        system_clock::time_point tp_recreated = system_clock::time_point{} + system_clock::duration(givenTicks);
        uint64 recreatedTicks = tp_now.time_since_epoch().count();
        Assert( givenTicks == recreatedTicks );  // has to be true now
    

    最后一个( uint64 到 timepoint )最让我困扰。需要的关键见解是:

    • (在 Win10 上)system_clock 使用 100 纳秒的时间分辨率。因此,不能直接将std::chrono::nanoseconds 添加到其原始时间点。 (std::chrono:system_clock_time_point)

    • 但是,由于刻度是 100 纳秒,因此也不能使用下一个更高的持续时间单位(微秒),因为它不能表示为整数值。

    • 可以使用 显式 强制转换为微秒,但这会降低滴答声的 0.1us 分辨率。

    • 正确的做法是使用system_clock自己的duration,直接用存储的tick值初始化。

    在我的搜索中,我发现以下资源最有帮助:

    【讨论】:

    • @HowardHinnant 我可以请你快速看一下这个,或者给我一个“好的”或者纠正我哪里错了?非常感谢,谢谢。
    • 我觉得不错。一些挑剔:numdentype period 的静态 constexpr 成员,因此如果需要,您可以确保在编译时完成此计算。它不会改变你得到的答案。表达式system_clock::time_point{} + system_clock::duration(givenTicks) 很有趣。这是正确的,但比我给出的答案更冗长:system_clock::time_point{system_clock::duration(givenTicks)}。我更喜欢你的答案,因为它更符合类型系统的代数。
    • 请注意不要在一个平台上写出time_point,然后在另一个平台上读入。在这种情况下,您可能会写出自纪元以来的纳秒,并在另一台机器上默默地将该数字重新解释为自纪元以来的其他一些单位。如果您确实需要这样做,您可以在写出之前将所有精度转换为nanoseconds,然后以纳秒为单位将其读回,并在读入后转换为system_clock::duration 精度。
    • C++ 如何将 std::chrono::time_point 转换为 long 并返回:stackoverflow.com/a/31258680/576911
    • 哦!为什么哦,为什么昨天我的搜索中没有出现那个帖子……嗯,至少我当时有一些边做边学的经验。
    【解决方案3】:

    参考手册是一个不错的地方:

    https://en.cppreference.com/w/cpp/chrono

    在这种情况下,您正在寻找:

    https://en.cppreference.com/w/cpp/chrono/clock_time_conversion

    因为您实际上使用的是“纪元”1/1/70 作为原点,毫秒作为单位的时钟。 然后只需对持续时间使用算术来执行您想要的截止操作:

    https://en.cppreference.com/w/cpp/chrono/duration

    每个链接页面底部都有代码示例。

    【讨论】:

    • 感谢您的建议。我查看了这些参考资料,但我找不到的示例是“如何在给定特定 uint64 值的情况下”从中创建时间点? (也许我只是瞎了眼……)
    猜你喜欢
    • 1970-01-01
    • 2018-09-11
    • 2021-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多