【问题标题】:std::chrono::high_resolution_clock based frame timer基于 std::chrono::high_resolution_clock 的帧计时器
【发布时间】:2021-04-23 16:20:21
【问题描述】:

多年来,我一直在为帧计时器使用以下时钟定义:

    using frame_clock = std::conditional_t<
        std::chrono::high_resolution_clock::is_steady,
        std::chrono::high_resolution_clock,
        std::chrono::steady_clock>;

换句话说,我想要使用尽可能高的分辨率定义的时钟,但它必须单调递增。请注意,MSVC 目前使用以下别名来定义std::chrono::high_resolution_clock

    using high_resolution_clock = steady_clock;

因此,在 MSVC 上,我定义的别名将只使用 std::chrono::steady_clock。对于 libstdc++ 和 libc++ 不一定如此,因此使用别名。

最近,我在这里偶然发现了一个脚注:https://en.cppreference.com/w/cpp/chrono/high_resolution_clock

请注意,cppreference 明确不鼓励使用 std::chrono::high_resolution_clock。他们的理由是时钟因实现而异……但std::chrono::steady_clockstd::chrono::system_clock 不也是这样吗?例如,我找不到任何东西可以保证时钟之间的时钟周期必须以特定单位为单位。事实上,我理解这是设计使然

我的问题是,在使用std::chrono::high_resolution_clock 这么多年(对于帧计时器和基准测试)之后,我应该比我更关心吗?即使在这个网站上,我也看到了许多使用std::chrono::high_resolution_clock 的建议,尽管这个脚注说了什么。非常感谢您对这种差异的任何进一步见解,或可能导致问题的示例。

【问题讨论】:

  • 我想这引出了一个问题:你用frame_clock 做什么?
  • 这是一个帧计时器。因此,它用于必须将处理划分为帧的应用程序。例如,所有处理必须适合 3600 fps 帧的嵌入式实时应用程序,或在 60 fps 时更宽容的视频游戏。我也经常使用std::chrono::high_resolution_clock 进行基准测试,不过,我通常不太关心它在这些应用程序中是否稳定,因为我可以一遍又一遍地运行基准测试。
  • 在那些专用系统上,系统时钟多久更改一次?你做UTC闰秒吗?你启用了 dst 吗?
  • @YSC 有时我看到处理器标量或其他频率控制参数发生变化以帮助支持特定波特率或 ODR 的实现。我们没有使用 GHz 时钟速度来最大程度地减少错误,因此错误变得非常重要,而且我们处于安全关键型应用中。当我们遇到这个问题时,我们当然必须更新我们的系统时钟以解决处理器频率的差异并保持我们的帧时间不变。对于 GNSS 应用,我们确实会考虑闰秒。
  • 那可能还不错,是的。此外,这些更改可能导致的错误类型特别难以追踪,这无济于事。

标签: c++ chrono high-resolution-clock


【解决方案1】:

出于实际目的,您只有 3 个选择:

  • 要实时处理,您唯一的选择是std::system_clock(如果您想留在 C++ 中,确实存在操作系统级别的例程)
  • 对于测量间隔,如果你想留在 C++ 中,你必须使用std::steady_clock。没有任何实现可以提供比std::steady_clock 更高分辨率的稳定时钟
  • 如果您急于牺牲 C++ 一致性,上述可行的替代方法是直接使用 TSC 计数器。这是人们所能看到的最高分辨率,也是使用速度最快的。缺点是,如果您想测量时间单位而不是周期,则必须使用 CPU 周期率将周期转换为秒。

【讨论】:

  • 一般来说,除非平台对我特别友好,否则system_clock 是……嗯,在嵌入式应用程序中,这通常是我必须自己定义的东西。我们经常定义类似于操作系统提供的帧,因此我们通常不能依赖操作系统向我们提供这些信息。不过理论上,我想我同意你的看法。实际上,您的最后一点对于 RTOS 应用程序来说非常重要。你会使用 CPU 周期计数器和当前频率来实现 system_clock 吗?
  • @ChristopherMauer 老实说,我在嵌入式方面没有太多经验。您当然应该能够使用 TSC 作为系统时钟的时间,但我想,问题是您如何设置芯片通电时的初始时间?
  • 这是一个如何在 x86 上将 TSC 指令集成到 chrono 中的示例:stackoverflow.com/a/11485388/576911
  • @HowardHinnant 是的,这在现代 x86 上非常简单。
  • @ChristopherMauer 只需确保 ARM 具有单调的 TSC 并在多个 CPU 上同步。 x86 并非总是如此,但我对 ARM 并不熟悉。
【解决方案2】:

您所阅读的内容基本上是我在过去几年中一直提供的建议。

high_resolution_clock 并不危险。只是它相当无用。这是因为它总是别名为system_clocksteady_clock。所以你不妨选择system_clocksteady_clock,这样你就知道你得到的是哪一个。

steady_clock 总是is_steady == true。这是一个要求。另外system_clock 从不is_steady == true。这实际上不是必需的,但除非您的计算机有一个保持完美时间的时钟,否则它需要不时调整以将其设置为正确的时间。并且可以调整的时钟必须有is_steady == false

您的frame_clock 别名只是一种花哨的说法:

using frame_clock = std::chrono::steady_clock;

【讨论】:

  • 忍者。无论如何,我实际上是在将 OP 指向您过去的答案。
  • 如果你想取消删除你的帖子,我会投赞成票。 :-)
  • 为了后代,当然。 :)
  • @ChristopherMauer C++ 中时钟的激增让我感到困惑。我可以将自己视为只有两个时钟的客户:反映实时的最高分辨率和稳定且单调的最高分辨率。我不明白为什么我需要超过 2 个。
  • GPS 时间在物理上测量闰秒,但与 UTC 的计算方式不同。 GPS时间不是在每分钟标记61秒,而是滚动到下一分钟(保持稳定)。因此,它的“人类日历”在闰秒期间比 UTC 领先一秒。例如,这里是用于比较的当前 UTC 和 GPS 时间:leapsecond.com/java/gpsclock.htm
【解决方案3】:

是的,您应该担心。 high_resolution_clock 是实现定义的。不要让实现选择,直接使用steady_clock

Howard Hinnant 写了一篇很棒的 comparison between steady and system clockswishes he had never added high_resolution_clock in the first place

和以前一样,坚持直接使用std::chrono::steady_clock,而不是让实现选择。

【讨论】:

    猜你喜欢
    • 2014-03-23
    • 2017-09-02
    • 2017-08-31
    • 2015-05-14
    • 1970-01-01
    • 1970-01-01
    • 2015-06-04
    • 2016-09-22
    • 2017-11-25
    相关资源
    最近更新 更多