2

我正在使用 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    
   }    
}
4

4 回答 4

7

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

视窗:

但是,有一种方法可以改善这一点,一种降低粒度的方法:多媒体计时器。特别是获取和设置定时器分辨率将揭示一种增加系统中断频率的方法。

编码:

#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 的情况看起来有些不同。看到这个得到一个想法。Linux 使用函数getnstimeofday (自纪元以来的秒数)混合 CMOS 时钟信息和使用函数timekeeping_get_ns来自高频计数器的信息(微秒)。这不是微不足道的,并且在准确性方面值得怀疑,因为这两个来源都由不同的硬件支持。这两个源不是锁相的,因此每秒可以获得多于/少于一百万微秒。

于 2012-11-01T11:42:59.020 回答
1

Windows 系统时钟每隔几毫秒就会滴答一次——在你的情况下是每秒 64 次,所以当它滴答作响时,它会将系统时间增加 15.625 毫秒。

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

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

于 2012-11-01T11:46:52.830 回答
1

我建议您查看 C++11<chrono>标头。

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

这里提到的滴答周期是时钟更新的频率。如果我们查看更多细节

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

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

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

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

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

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

于 2012-11-01T14:15:12.017 回答
0

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

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

然后有一些时间段,系统没有安排它执行,因此无法打印值。

如果您想要做的是每微秒执行一次,那么这可以通过在高性能硬件上运行的一些实时操作系统来实现。

于 2012-11-01T11:04:07.477 回答