为啥微秒时间戳使用(私有)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 上的 QueryPerformanceFrequency
、 QueryPerformanceCounter
函数。一个链接: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 <chrono>
标头。
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()重复,即纪元的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Node.js 中获取当前日期和微秒的时间戳? [复制]
从 unix 时间戳获取 ISOString(以微秒为单位)
Qt 时间戳从 Epoch(1970) 到 2000 年开始的 Postgresql 时间戳。如何添加偏移量并将毫秒转换为微秒?