【问题标题】:Consistent Timestamping in C++ with std::chrono使用 std::chrono 在 C++ 中使用一致的时间戳
【发布时间】:2016-03-20 13:12:26
【问题描述】:

我正在使用以下代码块在我的程序中记录时间戳:

// Taken at relevant time
m.timestamp = std::chrono::high_resolution_clock::now().time_since_epoch();


// After work is done
std::size_t secs = std::chrono::duration_cast <std::chrono::seconds> (timestamp).count();
std::size_t nanos = std::chrono::duration_cast<std::chrono::nanoseconds> (timestamp).count() % 1000000000;
std::time_t tp = (std::time_t) secs;

std::string mode;
char ts[] = "yyyymmdd HH:MM:SS";
char format[] = "%Y%m%d %H:%M:%S";
strftime(ts, 80, format, std::localtime(&tp));

std::stringstream s;
s << ts << "." << std::setfill('0') << std::setw(9) << nanos 
  << " - " << message << std::endl;
return s.str();

我将这些与准确的远程源记录的时间戳进行比较。当绘制时间戳差异且未启用 ntp 时,一天中会出现线性漂移(每 30 秒左右有 700 微秒)。

校正线性漂移后,我发现存在非线性分量。它可以在几个小时内漂移数百微秒。

第二张图看起来与使用与上述相同的方法拍摄的图相似,但启用了 NTP。预计数据中会出现较大的垂直峰值,但最小值的摆动令人惊讶。

有没有办法获得更精确的时间戳,但保留微秒/纳秒分辨率?如果时钟以可预测的方式偏离实际时间也没关系,但时间戳需要在很长一段时间内保持内部一致。

【问题讨论】:

  • 这不是一个真正的 C++ 问题,因为 std::chrono 函数只是从您的操作系统/硬件获取时间。它似乎更有可能与您的硬件有关。
  • 上次我查看 C++(尤其是 Microsoft)支持的“高分辨率计时器”时“不确定”尽管我试图实现不同的目标,但此 Q/A stackoverflow.com/questions/26003413/… 可能很有趣

标签: c++ clock ntp chrono


【解决方案1】:

high_resolution_clock 与“当前时间”没有保证关系。您的系统可能会将high_resolution_clock 别名为system_clock。这意味着您可能会也可能不会以这种方式使用high_resolution_clock

使用system_clock。然后告诉我们情况是否发生了变化(可能没有)。

还有,更好的风格:

using namespace std::chrono;
auto timestamp = ... // however, as long as it is based on system_clock
auto secs = duration_cast <seconds> (timestamp);
timestamp -= secs;
auto nanos = duration_cast<nanoseconds> (timestamp);
std::time_t tp = system_clock::to_time_t(system_clock::time_point{secs});
  • 尽可能长时间地使用计时类型系统。
  • 使用 chrono 类型系统为您进行转换和算术运算。
  • 使用system_clock::to_time_t 转换为time_t

但最终,以上都不会改变您的任何结果。 system_clock 只是要与操作系统对话(例如调用 gettimeofday 或其他)。

如果您可以设计一种更准确的方法来告诉您系统上的时间,您可以将该解决方案封装在“与计时兼容的时钟”中,以便您可以继续使用计时持续时间的类型安全和转换因子和 time_points。

struct my_super_accurate_clock
{
    using rep        = long long;
    using period     = std::nano;  // or whatever?
    using duration   = std::chrono::duration<rep, period>;
    using time_point = std::chrono::time_point<my_super_accurate_clock>;

    static const bool is_steady = false;

    static time_point now();  // do super accurate magic here
};

【讨论】:

  • 我认为 high_resolution_clock 对我来说是 system_clock 的别名(运行 CentOS Linux)。将时钟更改为 system_clock 会产生具有相同分辨率的时间戳。需要一些时间来测量一天的漂移。
  • 好的,gcc 的实现已知将high_resolution_clock 别名为system_clock。因此,更正此问题不会对您产生任何影响,只会让您的代码更便携。
  • 感谢清理版。是的,我认为 VS 使用相同的别名。但是,如果我正确理解了文档,那么在使用 stable_clock (没有转换为挂钟时间)的系统上,对 time_t 的强制转换将是无稽之谈。我很惊讶我在系统时钟上获得了纳秒级分辨率。
  • VS 和 libc++ 别名 high_resoluiton_clocksteady_clock。你是对的,steady_clock 与挂钟时间无关。它就像一个秒表(不会停止)。非常适合计时。
  • Fwiw,这是将system_clock::time_point 分解为日期和小时的另一种方法:分钟:秒:纳秒:howardhinnant.github.io/date_v2.html
【解决方案2】:

问题在于,除非您的机器非常不寻常,否则底层硬件根本无法能够提供特别可靠的时间测量(至少在您正在查看的刻度上)。

无论是在您的数字手表还是工作站上,大多数电子时钟信号都是由crystal oscillator 在内部生成的。这种晶体在其“理想”频率附近具有长期(年)和短期(分钟)的变化,其中最大的短期成分是随温度的变化。花哨的实验室设备将有类似crystal oven 的东西,它试图将晶体保持在恒定温度(高于环境温度)以最大限度地减少与温度相关的漂移,但我从未在商品计算硬件上看到过类似的东西。

您在两个图表中以不同的方式看到晶体误差的影响。第一张图简单地显示了您的晶体在与真实时间的偏差较大的情况下滴答作响,这可能是由于制造过程中的可变性(它总是那么糟糕)或长期漂移(随着时间的推移而变得如此)。启用 NTP 后,与 true 的“恒定”或平均偏移量很容易更正,因此您会期望在很长一段时间内平均零偏移量(实际上是由上面的最小下降所追踪的线且低于零)。

但是,在这种规模下,您会看到较小的短期变化效果。 NTP 会定期启动并尝试“修复它们”,但短期漂移始终存在并且总是在改变方向(您甚至可以检查升高或降低环境温度的影响并在图表中看到它)。

您无法避免摆动,但您或许可以增加 NTP 调整频率,以使其与实时更紧密地耦合。不过,您的确切要求并不完全清楚。比如你提到:

如果时钟在可预测的范围内偏离实际时间也没关系 方式,但时间戳需要在内部保持一致 很长一段时间。

“内部一致”是什么意思?如果您可以接受任意漂移,只需使用您现有的时钟,无需调整 NTP。如果您想要像“在大时间范围内”跟踪实时的时间(即,它不会不同步),为什么可以将您的内部时钟与您的定期轮询结合使用? “外部源”,并以平滑的方式更改调整因子,使您在明显的时间没有“跳跃”。这基本上是对 NTP 的重新发明,但至少它会完全在应用程序控制之下。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-02
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多