为啥微秒时间戳使用(私有)gettimeoftheday()重复,即纪元

Posted

技术标签:

【中文标题】为啥微秒时间戳使用(私有)gettimeoftheday()重复,即纪元【英文标题】:Why is microsecond timestamp is repetitive using (a private) gettimeoftheday() i.e. epoch为什么微秒时间戳使用(私有)gettimeoftheday()重复,即纪元 【发布时间】:2012-10-21 22:50:30 【问题描述】:

我正在使用 gettimeofday() 连续打印微秒。正如程序输出中给出的那样,您可以看到时间不是更新微秒间隔,而是重复某些样本,然后增量不是以微秒为单位,而是以毫秒为单位。

while(1)

  gettimeofday(&capture_time, NULL);
  printf(".%ld\n", capture_time.tv_usec);

程序输出:

.414719
.414719
.414719
.414719
.430344
.430344
.430344
.430344

 e.t.c

我希望输出按顺序递增,

.414719
.414720
.414721
.414722
.414723

.414723, .414723+x, .414723+2x, .414723 +3x + ...+ .414723+nx

当我从 capture_time.tv_usec 获取微秒时,它似乎没有刷新。

================================== //完整程序

#include <iostream>
#include <windows.h>
#include <conio.h>
#include <time.h>
#include <stdio.h>

#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
#else
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
#endif

struct timezone 

  int  tz_minuteswest; /* minutes W of Greenwich */
  int  tz_dsttime;     /* type of dst correction */
;

timeval capture_time;  // structure

int gettimeofday(struct timeval *tv, struct timezone *tz)

  FILETIME ft;
  unsigned __int64 tmpres = 0;
  static int tzflag;

  if (NULL != tv)
  
    GetSystemTimeAsFileTime(&ft);

    tmpres |= ft.dwHighDateTime;
    tmpres <<= 32;
    tmpres |= ft.dwLowDateTime;

    /*converting file time to unix epoch*/
    tmpres -= DELTA_EPOCH_IN_MICROSECS; 
    tmpres /= 10;  /*convert into microseconds*/
    tv->tv_sec = (long)(tmpres / 1000000UL);
    tv->tv_usec = (long)(tmpres % 1000000UL);
  

  if (NULL != tz)
  
    if (!tzflag)
    
      _tzset();
      tzflag++;
    

    tz->tz_minuteswest = _timezone / 60;
    tz->tz_dsttime = _daylight;
  

  return 0;


int main()

   while(1)
       
    gettimeofday(&capture_time, NULL);     
    printf(".%ld\n", capture_time.tv_usec);// JUST PRINTING MICROSECONDS    
       

【问题讨论】:

我假设您拥有一台可以每秒执行大量指令的现代计算机(实际上是毫秒 - 提示),也许您应该看看精度更高的计时器? 您的问题有点令人困惑,因为它实际上谈论的不是 POSIX 标准 gettimeofday 函数,而是 Win32 API 调用 (msdn.microsoft.com/en-us/library/windows/desktop/…)。请编辑您的问题和标题以反映您正在谈论的问题。另请注意,这些都不能保证它们的时钟分辨率。 嘿,你为什么不试试硬件时钟。检查 MSDN 上的 QueryPerformanceFrequencyQueryPerformanceCounter 函数。一个链接:msdn.microsoft.com/en-us/library/windows/desktop/… 它以 15.625 毫秒或 1/64 秒为增量,这是 Windows 机器上的常用时钟中断率。您永远无法获得稳定的递增值,printf() 需要的时间超过一微秒。 @Mat: 1st: 您的 msdn 链接没有内容。第二:Osaid 建立了一个private gettimeofday,虽然很奇怪。但它似乎清晰的窗口(包括)。 【参考方案1】:

您观察到的时间变化是 0.414719 秒到 0.430344 秒。差异为 15.615 毫秒。数字的表示是微秒这一事实意味着它增加了 1 微秒。事实上,我预计 15.625 毫秒。这是标准硬件上的系统时间增量。我仔细观察了here 和here。 这称为系统时间的粒度

Windows:

但是,有一种方法可以改善这种情况,一种降低粒度的方法:Multimedia Timers。特别是Obtaining and Setting Timer Resolution 将公开一种增加系统中断频率的方法。

代码:

#define TARGET_PERIOD 1         // 1-millisecond target interrupt period


TIMECAPS tc;
UINT     wTimerRes;

if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) 
// this call queries the systems timer hardware capabilities
// it returns the wPeriodMin and wPeriodMax with the TIMECAPS structure

  // Error; application can't continue.


// finding the minimum possible interrupt period:

wTimerRes = min(max(tc.wPeriodMin, TARGET_PERIOD ), tc.wPeriodMax);
// and setting the minimum period:

timeBeginPeriod(wTimerRes); 

这将强制系统以最大中断频率运行。作为结果 系统时间的更新也会更频繁,在大多数系统上系统时间增量的粒度为close to 1 milisecond

当您应该得到超出此范围的分辨率/粒度时,您必须查看QueryPerformanceCounter。但是,在较长时间内使用它时要小心使用。此计数器的频率可以通过调用QueryPerformanceFrequency 获得。操作系统将此频率视为常数,并将始终给出相同的值。但是,某些硬件会产生此频率,并且真实频率与给定值不同。它有一个偏移量,并显示出热漂移。因此,应假定误差在几微秒到几微秒/秒的范围内。有关这方面的更多详细信息,请参见上面的第二个“此处”链接。

Linux:

Linux 的情况看起来有些不同。请参阅this 了解一下。 Linux 使用函数getnstimeofday(自纪元以来的秒数)混合CMOS时钟的信息和使用函数timekeeping_get_ns的高频计数器(微秒)的信息。这不是微不足道的,并且在准确性方面值得怀疑,因为这两个来源都由不同的硬件支持。这两个源没有锁相,因此每秒可能多于/少于一百万微秒。

【讨论】:

我明白以微秒为单位的表示并不代表以微秒为单位的增量。我将研究粒度方面。 @Osaid:只需使用我提供的代码here 调查GetSystemTimeAsFileTime 的粒度即​​可。 令人毛骨悚然的是,Winpcap(PacketCapture 库)成功地使用了 epoch 并为捕获的数据包获得了高达一微秒的准确时间,尽管粒度很细。我已经成功地使用它的 (header->ts.tv_usec) 来实现我的时间戳目标,并通过并行运行 wireshark 进行了验证。两个时间都以 2 微秒的恒定间隔相匹配。 @Osaid:时间戳,例如在pcap_next_ex(...)的头参数中返回的时间戳由适配器填充。没有适配器就无法运行 pcap。 因此:如果有提供更好分辨率的硬件,您可能会获得更好的分辨率。提示:有许多可用于 PC 的高分辨率硬件时钟。例如GPS cards 可以提供 20ns 的精度。 @Osaid:我们通常认为系统时间更新是定期发生的。这是错误的。有些系统的中断周期与时间增量不匹配。在这些情况下,应用复杂的算法来查找拍频并对其进行校正,有时通过不同的时间增量,有时通过其中两个进行。另外:当系统时间调整发生时,增量会有所不同。有关系统时间调整的更多详细信息,请参阅GetSystemTimeAdjustment。【参考方案2】:

Windows 系统时钟仅每隔几毫秒计时一次 - 在您的情况下每秒 64 次,因此当它计时时,系统时间会增加 15.625 毫秒。

解决方案是使用比系统时间更高分辨率的计时器 (QueryPerformanceCounter)。

但是,您仍然看不到 .414723、.414723+x、.414723+2x、.414723 +3x + ...+ .414723+nx,因为您的代码不会在每个 x 中运行一次微秒。它会尽可能快地运行,但没有什么特别的理由应该始终保持恒定速度,或者如果是,那么它就是整数微秒。

【讨论】:

好的,谁能告诉我 31249 us 和 15625 us 一样有什么意义?【参考方案3】:

我建议你查看 C++11 &lt;chrono&gt; 标头。

high_resolution_clock (C++11) 可用时钟周期最短的时钟

这里提到的tick period是时钟更新的频率。如果我们查看more details:

template<
     class Rep,
     class Period = std::ratio<1>
> class duration;

类模板std::chrono::duration代表一个时间间隔。

它由 Rep 类型的滴答计数和滴答周期组成,其中滴答周期是编译时有理常数,表示从一个滴答到下一个滴答的秒数。 p>

以前,像 gettimeofday 这样的函数会给你一个以微秒表示的时间,但是它们完全无法告诉你刷新这个时间表达式的时间间隔。

在 C++11 标准中,此信息现在是明确的,以明确表示时间的单位与滴答周期之间没有关系。因此,您绝对需要将两者都考虑在内。

tick 周期在您想要测量接近它的持续时间时非常重要。如果您希望测量的持续时间低于刻度周期,那么您将像观察到的那样“离散地”测量它:0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, ...我建议在这一点上谨慎。

【讨论】:

【参考方案4】:

这是因为运行您的代码的进程并不总是计划执行。

虽然它确实如此,但它会快速循环,每微秒打印多个值 - 这在现代 CPU 上是一个相对较长的时间段。

在某些时间段内,系统没有安排它执行,因此无法打印值。

如果您希望每微秒执行一次,这可能通过一些在高性能硬件上运行的实时操作系统来实现。

【讨论】:

感谢您的及时回复。我不想每微秒执行一个进程,但我希望以微秒为单位从系统实时更新 TIME,以便我可以将其用作时间戳。 我们可以看到,Wire Shark 在 Windows PC 上运行时确实显示了长达纳秒(数据包时间戳)的时间。 抱歉,这个答案没有解释观察到的行为。

以上是关于为啥微秒时间戳使用(私有)gettimeoftheday()重复,即纪元的主要内容,如果未能解决你的问题,请参考以下文章

SQL 通过微秒时间戳获取过去几个月的行?

如何在 Node.js 中获取当前日期和微秒的时间戳? [复制]

python的时间戳(微秒)

从 unix 时间戳获取 ISOString(以微秒为单位)

Qt 时间戳从 Epoch(1970) 到 2000 年开始的 Postgresql 时间戳。如何添加偏移量并将毫秒转换为微秒?

mysql5.5时间戳能不能精确到毫秒?