【问题标题】:What is the best way to do time stamping?做时间戳的最佳方法是什么?
【发布时间】:2011-04-22 01:46:57
【问题描述】:

好的,所以我想做一些时间戳到 uS ...... mS 也可以......但是在研究这个时,我在想什么是做时间戳的最佳实践?在这种情况下,我的假设是使用了 C/C++ 代码。我知道内部时钟有各种分辨率等......但我对需要注意的事情很感兴趣......这样的答案将有助于解决我可能还不知道的事情。

一个好的时间格式应该是什么样的? 你对 Posix 时间函数有什么看法? 我想要一个纹理表示和一个内部表示? 什么是最简单的方法可以轻松地添加和减去时间?

【问题讨论】:

  • 时间戳是什么,目的是什么?
  • 很多东西......事件消息......来自串行总线的数据。

标签: c++ c time timestamp


【解决方案1】:

新的C++0x,在它的各种改进中,终于带来了一个成熟的time 库。

它主要是为支持thread 而开发的(表示各种try_lock 和al 的持续时间/时间限制),但它是一颗真正闪亮的珍珠。您可以找到原始提案 here 和 Bjarne 关于它的条目 here

显然,如果您希望将此时间与外部源进行比较,则它依赖于您的系统是否正确同步:使用 NTP。我还建议使用 UTC 时间,这样更容易在公共时区表示所有时间,无需夏令时。

至于表示本身,我会选择人类可读的。机器解析任何一种格式都一样容易,但对人类来说却不是这样,谁在调查?

[1] 2010/10/07 06:54:36.123456
[2] 2010 OCT 07 06:54:36.123456
[3] 86456156456.123456          // garbage I made up ;)

第二种格式可以消除月份和日期的歧义,而不会造成任何混淆。这是我最喜欢的一个,但我经常看到第一个。

【讨论】:

  • 第一种格式几乎是 ISO 标准,并且可以与更多基于文本的解析器进行更好的互操作。我认为部分秒可能会或可能不会被完全解析。它们可能会变圆或被切掉。
【解决方案2】:

要注意什么?

通过 NTP 使您的系统保持同步,这样您就可以关联来自多个系统的日志。

如果您在内部存储时间戳,请将其存储为自 1970 UTC 以来的秒数(mS 或 uS 可能使用小数)。这是非常标准的,在表示上,您可以轻松地将其转换为任何时区的本地时间(如果您必须处理多个时区,则很有用)。此外,UTC 没有夏令时,这避免了一些歧义(但不是其他的,仍然有闰秒)。

如果您只想要日志文件的时间戳,您可以编写已转换为某种人类可读格式的时间戳。在处理多个时区和避免夏令时歧义时,UTC 在这里仍然有其优势。

如果您需要更准确的信息,请考虑 DJB 的 libtai,或者根据您的需要,使用 CLOCK_MONOTONIC 的 POSIX clock_gettime() 可能就足够了。

【讨论】:

    【解决方案3】:

    没有任何便携式时间函数可以可靠地为您提供优于 10 毫秒的分辨率。其他响应者引用的函数 gettimeofday() 具有系统相关的分辨率,我相信通常为 1 或 10 毫秒。此外,即使是 90% 的非便携式时间函数都会涉及到 ring 0(数千个时钟滴答),这意味着执行函数调用可能需要 1-2 uS!

    唯一适用于所有英特尔平台、为您提供亚微秒级分辨率且每次调用花费不到 10 纳秒的方法是汇编指令“rdtsc”(或内在的 __rdtsc())。

    【讨论】:

    • rdtsc 有很多潜在的问题。在多 CPU/多核上,每个核的 TSC 可能不同。在 CPU 频率变化期间,TSC 可能会改变频率。在挂起/恢复后它可能包含随机值......另见stackoverflow.com/questions/3835111/…
    • 在 Windows 上,时钟滴答通常被记录为在 10 毫秒到 16 毫秒的范围内,尽管 15.6 左右是常见的。 rdtsc 有 ninjalj 描述的问题,但对于固定时钟的服务器环境,它可以与 CPUID 和核心不同步的测量相结合以关联核心。
    【解决方案4】:

    查看你的手册页以获取 gettimeofday

    int gettimeofday(struct timeval *tp, void *);
    
    long    tv_sec;    /* seconds since Jan. 1, 1970 */
         long    tv_usec;   /* and microseconds */
    

    您可以将时间存储为 struct timeval,有用于对 struct timeval 值进行加法和减法的宏。您可以使用 ctime(tv_sec) 获取 ascii 字符串。 如果您需要使用 usec 创建字符串,请尝试以下操作:

    char *now(void)  /* time to /1000ths of a second */
    {
        struct timeval tv={0,0};
        int i=gettimeofday(&tv, NULL);
        static char retval[64]={0x0};
        char tmp[16]={0x0};
    
        if(i<0)
            return NULL;
        sprintf(tmp, "%.3f", (double)tv.tv_usec / (double)1000000);
        strftime(retval, sizeof(retval),
                 "%Y-%m-%d %H:%M:%S.",
                 localtime(&tv.tv_sec));
        strcat(retval, &tmp[2]);
    
        return retval;
    }
    

    【讨论】:

      【解决方案5】:

      在我看来,最好的方法是将基于纪元的表示保持为整数。最标准的一种是自 1970 年以来保持秒数。由于您想保持更高的分辨率,您可以扩展它并自 1970 年以来保持微秒或毫秒。为了实现这一点,您至少需要使用 64 位整数 (长长)。

      posix gettimeofday() 将为您提供高达微秒的分辨率,您可以将其与 gmtime_r() 函数结合使用以获取其余标记。对于 windows,我使用 GetSystemTime() 函数来获取分辨率高达毫秒的时间(实际上,我认为它是几十毫秒)。下面的代码片段展示了这种方法(不是我的时间单位是纳秒)。

        LgrDate rtn;
      #ifdef _WIN32
        SYSTEMTIME sys;
        GetSystemTime(&sys);
        rtn.setDate(
           sys.wYear,
           sys.wMonth,
           sys.wDay);
        rtn.setTime(
           sys.wHour,
           sys.wMinute,
           sys.wSecond,
           sys.wMilliseconds*uint4(nsecPerMSec));
      #else
        struct timeval time_of_day;
        struct tm broken_down;
        gettimeofday(&time_of_day,0);
        gmtime_r(
           &time_of_day.tv_sec,
           &broken_down);
        rtn.setDate(
           broken_down.tm_year + 1900,
           broken_down.tm_mon + 1,
           broken_down.tm_mday);
        rtn.setTime(
           broken_down.tm_hour,
           broken_down.tm_min,
           broken_down.tm_sec,
           time_of_day.tv_usec * nsecPerUSec);
      #endif
        return rtn;
      

      【讨论】:

        猜你喜欢
        • 2020-11-13
        • 1970-01-01
        • 1970-01-01
        • 2011-05-17
        • 2019-01-27
        • 1970-01-01
        • 2023-04-04
        • 2010-10-09
        • 2010-12-02
        相关资源
        最近更新 更多