windows C++ 窗口时间
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1497702/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ windows time
提问by Boris Raznikov
I have a problem in using time. I want to use and get microseconds on windows using C++.
我在使用时间方面有问题。我想在使用 C++ 的 Windows 上使用和获取微秒。
I can't find the way.
我找不到路
回答by bronekk
The "canonical" answer was given by unwind:
“规范”答案是由unwind给出的:
One popular way is using the QueryPerformanceCounter() call.
一种流行的方法是使用 QueryPerformanceCounter() 调用。
There are however few problems with this method:
然而,这种方法几乎没有问题:
- it's intended for measurement of time intervals, not time. This means you have to write code to establish "epoch time" from which you will measure precise intervals. This is called calibration.
- As you calibrate your clock, you also need to periodically adjust it so it's never too much out of sync (this is called drift) with your system clock.
QueryPerformanceCounter
is not implemented in user space; this means context switch is needed to call kernel side of implementation, and that is relatively expensive (around 0.7 microsecond). This seems to be required to support legacy hardware.
- 它用于测量时间间隔,而不是时间。这意味着您必须编写代码来建立“纪元时间”,从中可以测量精确的间隔。这称为校准。
- 在校准时钟时,您还需要定期调整它,这样它就不会与系统时钟同步太多(这称为漂移)。
QueryPerformanceCounter
未在用户空间中实现;这意味着需要上下文切换来调用实现的内核端,这相对昂贵(大约 0.7 微秒)。这似乎是支持旧硬件所必需的。
Not all is lost, though. Points 1. and 2. are something you can do with a bit of coding, 3. can be replaced with direct call to RDTSC (available in newer versions of Visual C++ via __rdtsc()
intrinsic), as long as you know accurate CPU clock frequency. Although, on older CPUs, such call would be susceptible to changes in cpu internal clock speed, in all newer Intel and AMD CPUs it is guaranteed to give fairly accurate results and won't be affected by changes in CPU clock (e.g. power saving features).
不过,并不是所有的都丢失了。点 1. 和 2. 是您可以通过一些编码来完成的事情,3. 可以替换为直接调用 RDTSC(通过__rdtsc()
内部函数在较新版本的 Visual C++ 中可用 ),只要您知道准确的 CPU 时钟频率。尽管在较旧的 CPU 上,这种调用可能会受到 CPU 内部时钟速度变化的影响,但在所有较新的 Intel 和 AMD CPU 中,它可以保证给出相当准确的结果,并且不会受到 CPU 时钟变化的影响(例如省电功能) )。
Lets get started with 1. Here is data structure to hold calibration data:
让我们从 1 开始。这是保存校准数据的数据结构:
struct init
{
long long stamp; // last adjustment time
long long epoch; // last sync time as FILETIME
long long start; // counter ticks to match epoch
long long freq; // counter frequency (ticks per 10ms)
void sync(int sleep);
};
init data_[2] = {};
const init* volatile init_ = &data_[0];
Here is code for initial calibration; it has to be given time (in milliseconds) to wait for the clock to move; I've found that 500 milliseconds give pretty good results (the shorter time, the less accurate calibration). For the purpose of callibration we are going to use QueryPerformanceCounter()
etc. You only need to call it for data_[0]
, since data_[1]
will be updated by periodic clock adjustment (below).
这是初始校准的代码;必须给它时间(以毫秒为单位)来等待时钟移动;我发现 500 毫秒的结果非常好(时间越短,校准越不准确)。出于校准目的,我们将使用QueryPerformanceCounter()
等。您只需将其调用为data_[0]
,因为data_[1]
它将通过周期性时钟调整(如下)进行更新。
void init::sync(int sleep)
{
LARGE_INTEGER t1, t2, p1, p2, r1, r2, f;
int cpu[4] = {};
// prepare for rdtsc calibration - affinity and priority
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
SetThreadAffinityMask(GetCurrentThread(), 2);
Sleep(10);
// frequency for time measurement during calibration
QueryPerformanceFrequency(&f);
// for explanation why RDTSC is safe on modern CPUs, look for "Constant TSC" and "Invariant TSC" in
// Intel(R) 64 and IA-32 Architectures Software Developer's Manual (document 253668.pdf)
__cpuid(cpu, 0); // flush CPU pipeline
r1.QuadPart = __rdtsc();
__cpuid(cpu, 0);
QueryPerformanceCounter(&p1);
// sleep some time, doesn't matter it's not accurate.
Sleep(sleep);
// wait for the system clock to move, so we have exact epoch
GetSystemTimeAsFileTime((FILETIME*) (&t1.u));
do
{
Sleep(0);
GetSystemTimeAsFileTime((FILETIME*) (&t2.u));
__cpuid(cpu, 0); // flush CPU pipeline
r2.QuadPart = __rdtsc();
} while(t2.QuadPart == t1.QuadPart);
// measure how much time has passed exactly, using more expensive QPC
__cpuid(cpu, 0);
QueryPerformanceCounter(&p2);
stamp = t2.QuadPart;
epoch = t2.QuadPart;
start = r2.QuadPart;
// calculate counter ticks per 10ms
freq = f.QuadPart * (r2.QuadPart-r1.QuadPart) / 100 / (p2.QuadPart-p1.QuadPart);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
SetThreadAffinityMask(GetCurrentThread(), 0xFF);
}
With good calibration data you can calculate exact time from cheap RDTSC (I measured the call and calculation to be ~25 nanoseconds on my machine). There are three things to note:
有了良好的校准数据,您就可以从廉价的 RDTSC 计算出准确的时间(我在我的机器上测得的调用和计算时间约为 25 纳秒)。有以下三点需要注意:
return type is binary compatible with FILETIME structure and is precise to 100ns , unlike
GetSystemTimeAsFileTime
(which increments in 10-30ms or so intervals, or 1 millisecond at best).in order to avoid expensive conversions integer to double to integer, the whole calculation is performed in 64 bit integers. Even though these can hold huge numbers, there is real risk of integer overflow, and so
start
must be brought forward periodically to avoid it. This is done in clock adjustment.we are making a copy of calibration data, because it might have been updated during our call by clock adjustement in another thread.
返回类型与 FILETIME 结构二进制兼容,并且精确到 100ns ,不像
GetSystemTimeAsFileTime
(以 10-30 毫秒左右的间隔递增,或者最多 1 毫秒)。为了避免将integer转换成double到integer的昂贵转换,整个计算都是在64位整数中进行的。尽管这些可以容纳大量数字,但确实存在整数溢出的风险,因此
start
必须定期提出以避免它。这是在时钟调整中完成的。我们正在制作校准数据的副本,因为它可能在我们调用期间通过另一个线程中的时钟调整进行了更新。
Here is the code to read current time with high precision. Return value is binary compatible with FILETIME, i.e. number of 100-nanosecond intervals since Jan 1, 1601.
这是高精度读取当前时间的代码。返回值与 FILETIME 二进制兼容,即自 1601 年 1 月 1 日以来 100 纳秒间隔的数量。
long long now()
{
// must make a copy
const init* it = init_;
// __cpuid(cpu, 0) - no need to flush CPU pipeline here
const long long p = __rdtsc();
// time passed from epoch in counter ticks
long long d = (p - it->start);
if (d > 0x80000000000ll)
{
// closing to integer overflow, must adjust now
adjust();
}
// convert 10ms to 100ns periods
d *= 100000ll;
d /= it->freq;
// and add to epoch, so we have proper FILETIME
d += it->epoch;
return d;
}
For clock adjustment, we need to capture exact time (as provided by system clock) and compare it against our clock; this will give us drift value. Next we use simple formula to calculate "adjusted" CPU frequency, to make our clock meet system clock at the time of next adjustment. Thus it is important that adjustments are called on regular intervals; I've found that it works well when called in 15 minutes intervals. I use CreateTimerQueueTimer
, called once at program startup to schedule adjustment calls (not demonstrated here).
对于时钟调整,我们需要捕获准确的时间(由系统时钟提供)并将其与我们的时钟进行比较;这会给我们漂移值。接下来我们用简单的公式计算“调整后的”CPU频率,使我们的时钟在下次调整时满足系统时钟。因此,定期进行调整很重要;我发现以 15 分钟为间隔调用它时效果很好。我使用CreateTimerQueueTimer
, 在程序启动时调用一次来安排调整调用(此处未演示)。
The slight problem with capturing accurate system time (for the purpose of calculating drift) is that we need to wait for the system clock to move, and that can take up to 30 milliseconds or so (it's a longtime). If adjustment is not performed, it would risk integer overflow inside function now()
, not to mention uncorrected drift from system clock. There is builtin protection against overflow in now()
, but we really don't want to trigger it synchronously in a thread which happened to call now()
at the wrong moment.
捕获准确系统时间(为了计算漂移)的一个小问题是我们需要等待系统时钟移动,这可能需要 30 毫秒左右(这是很长的时间)。如果不进行调整,函数内部会有整数溢出的风险now()
,更不用说未校正的系统时钟漂移。中有内置的溢出保护now()
,但我们真的不想在一个碰巧now()
在错误时刻调用的线程中同步触发它。
Here is the code for periodic clock adjustment, clock drift is in r->epoch - r->stamp
:
这是周期性时钟调整的代码,时钟漂移在r->epoch - r->stamp
:
void adjust()
{
// must make a copy
const init* it = init_;
init* r = (init_ == &data_[0] ? &data_[1] : &data_[0]);
LARGE_INTEGER t1, t2;
// wait for the system clock to move, so we have exact time to compare against
GetSystemTimeAsFileTime((FILETIME*) (&t1.u));
long long p = 0;
int cpu[4] = {};
do
{
Sleep(0);
GetSystemTimeAsFileTime((FILETIME*) (&t2.u));
__cpuid(cpu, 0); // flush CPU pipeline
p = __rdtsc();
} while (t2.QuadPart == t1.QuadPart);
long long d = (p - it->start);
// convert 10ms to 100ns periods
d *= 100000ll;
d /= it->freq;
r->start = p;
r->epoch = d + it->epoch;
r->stamp = t2.QuadPart;
const long long dt1 = t2.QuadPart - it->epoch;
const long long dt2 = t2.QuadPart - it->stamp;
const double s1 = (double) d / dt1;
const double s2 = (double) d / dt2;
r->freq = (long long) (it->freq * (s1 + s2 - 1) + 0.5);
InterlockedExchangePointer((volatile PVOID*) &init_, r);
// if you have log output, here is good point to log calibration results
}
Lastly two utility functions. One will convert FILETIME (including output from now()
) to SYSTEMTIME while preserving microseconds to separate int
. Other will return frequency, so your program can use __rdtsc()
directly for accurate measurements of time intervals (with nanosecond precision).
最后两个效用函数。一种将 FILETIME(包括来自 的输出now()
)转换为 SYSTEMTIME,同时保留微秒以分隔int
. 其他将返回频率,因此您的程序可以__rdtsc()
直接用于时间间隔的准确测量(具有纳秒精度)。
void convert(SYSTEMTIME& s, int &us, long long f)
{
LARGE_INTEGER i;
i.QuadPart = f;
FileTimeToSystemTime((FILETIME*) (&i.u), &s);
s.wMilliseconds = 0;
LARGE_INTEGER t;
SystemTimeToFileTime(&s, (FILETIME*) (&t.u));
us = (int) (i.QuadPart - t.QuadPart)/10;
}
long long frequency()
{
// must make a copy
const init* it = init_;
return it->freq * 100;
}
Well of course none of the above is more accuratethan your system clock, which is unlikely to be more accurate than few hundred milliseconds. The purpose of preciseclock (as opposed to accurate) as implemented above, is to provide single measure which can be used for both:
当然,以上没有一个比您的系统时钟更准确,它不太可能比几百毫秒更准确。的目的精确时钟(而不是准确的为实现以上,)是提供一种可以用于单一的措施二者:
- cheap and very accuratemeasurement of time intervals(not wall time),
- much less accurate, but monotonous and consistent with the above, measure of wall time
- 廉价且非常准确的时间间隔测量(不是墙上时间),
- 不太准确,但单调且与上述一致,墙上时间的测量
I think it does it pretty well. Example use are logs, where one can use timestamps not only to find time of events, but also reason about internal program timings, latency (in microseconds) etc.
我认为它做得很好。示例用途是日志,其中不仅可以使用时间戳来查找事件时间,还可以推断内部程序计时、延迟(以微秒为单位)等。
I leave the plumbing (call to initial calibration, scheduling adjustment) as an exercise for gentle readers.
我将管道(调用初始校准,安排调整)作为温和读者的练习。
回答by Davit Siradeghyan
You can use boost date time library.
您可以使用提升日期时间库。
You can use boost::posix_time::hours, boost::posix_time::minutes, boost::posix_time::seconds, boost::posix_time::millisec, boost::posix_time::nanosec
您可以使用 boost::posix_time::hours、boost::posix_time::minutes、boost::posix_time::seconds、boost::posix_time::millisec、boost::posix_time::nanosec
http://www.boost.org/doc/libs/1_39_0/doc/html/date_time.html
http://www.boost.org/doc/libs/1_39_0/doc/html/date_time.html
回答by unwind
One popular way is using the QueryPerformanceCounter()
call. This is useful if you need high-precision timing, such as for measuring durations that only take on the order of microseconds. I believe this is implemented using the RDTSC
machine instruction.
一种流行的方式是使用QueryPerformanceCounter()
调用。如果您需要高精度计时,这将非常有用,例如测量仅需要几微秒的持续时间。我相信这是使用RDTSC
机器指令实现的。
There might be issues though, such as the counter frequency varying with power-saving, and synchronization between multiple cores. See the Wikipedia link above for details on these issues.
但是可能存在一些问题,例如计数器频率随省电而变化,以及多核之间的同步。有关这些问题的详细信息,请参阅上面的维基百科链接。
回答by Frank Bollack
Take a look at the Windows APIs GetSystemTime()
/ GetLocalTime()
or GetSystemTimeAsFileTime()
.
看看 Windows APIs GetSystemTime()
/GetLocalTime()
或GetSystemTimeAsFileTime()
.
GetSystemTimeAsFileTime() expresses time in 100 nanosecond intervals, that is 1/10 of a microsecond. All functions provide the current time with in millisecond accuracy.
GetSystemTimeAsFileTime() 以 100 纳秒的间隔表示时间,即 1/10 微秒。所有功能都以毫秒精度提供当前时间。
EDIT:
编辑:
Keep in mind, that on most Windows systems the system time is only updated about every 1 millisecond. So even representing your time with microsecond accuracy makes it still necessary to acquire the time with such a precision.
请记住,在大多数 Windows 系统上,系统时间大约每 1 毫秒更新一次。因此,即使以微秒精度表示您的时间,仍然有必要以这种精度获取时间。
回答by Jonas B
Take a look at this: http://www.decompile.com/cpp/faq/windows_timer_api.htm
看看这个:http: //www.decompile.com/cpp/faq/windows_timer_api.htm
回答by Andrey Atapin
May be this can help:
可能这可以帮助:
NTSTATUS WINAPI NtQuerySystemTime(__out PLARGE_INTEGER SystemTime);
SystemTime [out] - a pointer to a LARGE_INTEGER structure that receives the system time. This is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).
SystemTime [out] - 指向接收系统时间的 LARGE_INTEGER 结构的指针。这是一个 64 位值,表示自 1601 年 1 月 1 日 (UTC) 以来 100 纳秒间隔的数量。