如何测量的时间在毫秒使用ANSI C?
-
21-08-2019 - |
题
只使用ANSI C,是否有任何方法来衡量毫秒的时间与精密或更多吗?我浏览的时间。h但我只找到第二个精密的功能。
解决方案
没有ANSI C功能,提供更好于1秒的时间分辨率,但POSIX function gettimeofday
提供微秒的决议。时钟的功能只措施的时间量是一个过程已经花费了执行和不准确,在许多系统。
你可以使用这一功能是这样的:
struct timeval tval_before, tval_after, tval_result;
gettimeofday(&tval_before, NULL);
// Some code you want to time, for example:
sleep(1);
gettimeofday(&tval_after, NULL);
timersub(&tval_after, &tval_before, &tval_result);
printf("Time elapsed: %ld.%06ld\n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec);
此返回 Time elapsed: 1.000870
在我的机器。
其他提示
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
我总是使用clock_gettime()函数,从CLOCK_MONOTONIC时钟返回时间。返回的时间是时间的量,以秒和纳秒,因为在过去的一些未指定的点,如历元的系统的启动。
#include <stdio.h>
#include <stdint.h>
#include <time.h>
int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}
int main(int argc, char **argv)
{
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
// Some code I am interested in measuring
clock_gettime(CLOCK_MONOTONIC, &end);
uint64_t timeElapsed = timespecDiff(&end, &start);
}
实施便携式解决方案
正如这里已经提到的,对于时间测量问题,没有足够精度的适当 ANSI 解决方案,我想写一下如何获得便携式的、如果可能的话,高分辨率时间测量解决方案。
单调时钟 vs. 单调时钟时间戳
一般来说,时间测量有两种方法:
- 单调时钟;
- 当前(日期)时间戳。
第一个使用单调时钟计数器(有时称为滴答计数器),它以预定义的频率对滴答进行计数,因此如果您有滴答值并且频率已知,则可以轻松地将滴答转换为经过的时间。实际上并不能保证单调时钟以任何方式反映当前系统时间,它还可能对系统启动后的滴答数进行计数。但它保证时钟始终以递增的方式运行,无论系统状态如何。通常频率与硬件高分辨率源绑定,这就是为什么它提供高精度(取决于硬件,但大多数现代硬件对于高分辨率时钟源没有问题)。
第二种方式提供基于当前系统时钟值的(日期)时间值。它也可能具有高分辨率,但有一个主要缺点:这种时间值会受到不同系统时间调整的影响,即时区更改、夏令时 (DST) 更改、NTP 服务器更新、系统休眠等。在某些情况下,您可能会得到负的经过时间值,这可能会导致未定义的行为。实际上,这种时间源不如第一种可靠。
因此,时间间隔测量的第一条规则是如果可能的话使用单调时钟。它通常具有高精度,并且设计可靠。
后备策略
在实施便携式解决方案时,值得考虑后备策略:如果可用,则使用单调时钟;如果系统中没有单调时钟,则回退到时间戳方法。
视窗
有一篇很棒的文章叫做 获取高分辨率时间戳 有关 Windows 上的时间测量的 MSDN,其中描述了您可能需要了解的有关软件和硬件支持的所有详细信息。要在 Windows 上获取高精度时间戳,您应该:
查询计时器频率(每秒滴答数) 查询性能频率:
LARGE_INTEGER tcounter; LARGE_INTEGER freq; if (QueryPerformanceFrequency (&tcounter) != 0) freq = tcounter.QuadPart;
定时器频率在系统启动时是固定的,因此您只需获取一次。
查询当前的刻度值 查询性能计数器:
LARGE_INTEGER tcounter; LARGE_INTEGER tick_value; if (QueryPerformanceCounter (&tcounter) != 0) tick_value = tcounter.QuadPart;
将刻度缩放到经过的时间,即到微秒:
LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);
根据 Microsoft 的说法,在大多数情况下,在 Windows XP 及更高版本上使用此方法不会有任何问题。但您也可以在 Windows 上使用两种后备解决方案:
- 获取刻度数 提供自系统启动以来经过的毫秒数。它每 49.7 天循环一次,因此在测量较长的间隔时要小心。
- 获取TickCount64 是 64 位版本
GetTickCount
, ,但从 Windows Vista 及更高版本开始可用。
操作系统 X (macOS)
OS X (macOS) 有自己的马赫绝对时间单位,代表单调时钟。最好的开始方式是苹果的文章 技术问答 QA1398:马赫绝对时间单位 它描述了(带有代码示例)如何使用 Mach 特定的 API 来获取单调刻度。还有一个关于它的本地问题称为 Mac OS X 中的clock_gettime 替代方案 最后可能会让您有点困惑如何处理可能的值溢出,因为计数器频率以分子和分母的形式使用。那么,一个简短的例子如何获取经过的时间:
获取时钟频率分子和分母:
#include <mach/mach_time.h> #include <stdint.h> static uint64_t freq_num = 0; static uint64_t freq_denom = 0; void init_clock_frequency () { mach_timebase_info_data_t tb; if (mach_timebase_info (&tb) == KERN_SUCCESS && tb.denom != 0) { freq_num = (uint64_t) tb.numer; freq_denom = (uint64_t) tb.denom; } }
您只需要这样做一次。
查询当前刻度值
mach_absolute_time
:uint64_t tick_value = mach_absolute_time ();
将刻度缩放到经过的时间,即到微秒,使用之前查询的分子和分母:
uint64_t value_diff = tick_value - prev_tick_value; /* To prevent overflow */ value_diff /= 1000; value_diff *= freq_num; value_diff /= freq_denom;
防止溢出的主要思想是在使用分子和分母之前将刻度缩小到所需的精度。由于初始计时器分辨率以纳秒为单位,因此我们将其除以
1000
获取微秒。您可以找到 Chromium 中使用的相同方法 time_mac.c. 。如果您确实需要纳秒精度,请考虑阅读 如何使用 mach_absolute_time 而不溢出?.
Linux 和 UNIX
这 clock_gettime
call 是在任何 POSIX 友好系统上的最佳方式。它可以查询不同时钟源的时间,我们需要的是 CLOCK_MONOTONIC
. 。并非所有系统都具有 clock_gettime
支持 CLOCK_MONOTONIC
, ,所以您需要做的第一件事就是检查其可用性:
- 如果
_POSIX_MONOTONIC_CLOCK
被定义为一个值>= 0
代表着CLOCK_MONOTONIC
可用; 如果
_POSIX_MONOTONIC_CLOCK
被定义为0
这意味着您还应该检查它在运行时是否有效,我建议使用sysconf
:#include <unistd.h> #ifdef _SC_MONOTONIC_CLOCK if (sysconf (_SC_MONOTONIC_CLOCK) > 0) { /* A monotonic clock presents */ } #endif
- 否则,不支持单调时钟,您应该使用后备策略(见下文)。
的用法 clock_gettime
非常简单:
获取时间值:
#include <time.h> #include <sys/time.h> #include <stdint.h> uint64_t get_posix_clock_time () { struct timespec ts; if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0) return (uint64_t) (ts.tv_sec * 1000000 + ts.tv_nsec / 1000); else return 0; }
我在这里将时间缩减到微秒。
以同样的方式计算与之前收到的时间值的差值:
uint64_t prev_time_value, time_value; uint64_t time_diff; /* Initial time */ prev_time_value = get_posix_clock_time (); /* Do some work here */ /* Final time */ time_value = get_posix_clock_time (); /* Time difference */ time_diff = time_value - prev_time_value;
最好的后备策略是使用 gettimeofday
称呼:它不是单调的,但它提供了相当好的分辨率。这个想法与 clock_gettime
, ,但要获得时间值,您应该:
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
uint64_t get_gtod_clock_time ()
{
struct timeval tv;
if (gettimeofday (&tv, NULL) == 0)
return (uint64_t) (tv.tv_sec * 1000000 + tv.tv_usec);
else
return 0;
}
同样,时间值被缩小到微秒。
SGI IRIX
IRIX 有 clock_gettime
调用,但缺少 CLOCK_MONOTONIC
. 。相反,它有自己的单调时钟源,定义为 CLOCK_SGI_CYCLE
你应该使用它而不是 CLOCK_MONOTONIC
和 clock_gettime
.
Solaris 和 HP-UX
Solaris 有自己的高分辨率定时器接口 gethrtime
它返回当前计时器值(以纳秒为单位)。尽管较新版本的 Solaris 可能具有 clock_gettime
, ,你可以坚持 gethrtime
如果您需要支持旧的 Solaris 版本。
用法很简单:
#include <sys/time.h>
void time_measure_example ()
{
hrtime_t prev_time_value, time_value;
hrtime_t time_diff;
/* Initial time */
prev_time_value = gethrtime ();
/* Do some work here */
/* Final time */
time_value = gethrtime ();
/* Time difference */
time_diff = time_value - prev_time_value;
}
HP-UX 缺乏 clock_gettime
, ,但它支持 gethrtime
您应该以与 Solaris 上相同的方式使用它。
贝奥斯
贝奥斯 还有自己的高分辨率定时器接口 system_time
它返回自计算机启动以来经过的微秒数。
用法示例:
#include <kernel/OS.h>
void time_measure_example ()
{
bigtime_t prev_time_value, time_value;
bigtime_t time_diff;
/* Initial time */
prev_time_value = system_time ();
/* Do some work here */
/* Final time */
time_value = system_time ();
/* Time difference */
time_diff = time_value - prev_time_value;
}
操作系统/2
操作系统/2 有自己的API来检索高精度时间戳:
查询计时器频率(每单位滴答声)
DosTmrQueryFreq
(对于 GCC 编译器):#define INCL_DOSPROFILE #define INCL_DOSERRORS #include <os2.h> #include <stdint.h> ULONG freq; DosTmrQueryFreq (&freq);
查询当前的刻度值
DosTmrQueryTime
:QWORD tcounter; unit64_t time_low; unit64_t time_high; unit64_t timestamp; if (DosTmrQueryTime (&tcounter) == NO_ERROR) { time_low = (unit64_t) tcounter.ulLo; time_high = (unit64_t) tcounter.ulHi; timestamp = (time_high << 32) | time_low; }
将刻度缩放到经过的时间,即到微秒:
uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);
实施示例
你可以看看 普利布系统 实现上述所有策略的库(有关详细信息,请参阅 ptimeprofiler*.c)。
timespec_get
从C11开始
返回最多纳秒,四舍五入到实现的分辨率。
看起来像是 ANSI 抄袭 POSIX' clock_gettime
.
例子:A printf
在 Ubuntu 15.10 上每 100 毫秒执行一次:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static long get_nanos(void) {
struct timespec ts;
timespec_get(&ts, TIME_UTC);
return (long)ts.tv_sec * 1000000000L + ts.tv_nsec;
}
int main(void) {
long nanos;
long last_nanos;
long start;
nanos = get_nanos();
last_nanos = nanos;
start = nanos;
while (1) {
nanos = get_nanos();
if (nanos - last_nanos > 100000000L) {
printf("current nanos: %ld\n", nanos - start);
last_nanos = nanos;
}
}
return EXIT_SUCCESS;
}
这 C11 N1570标准草案 7.27.2.5“timespec_get 函数说”:
如果base是time_utc,则将TV_SEC成员设置为秒数,因为实现了定义的时期,将其截断为整体值,并且TV_NSEC成员设置为纳秒的积分数,并将其四舍五入为系统时钟的分辨率。(321)
321)尽管一个结构时间佩克对象用纳秒分辨率描述了时间,但可用的分辨率是系统依赖性的,甚至可能大于1秒。
C++11也得到了 std::chrono::high_resolution_clock
: C++ 跨平台高分辨率定时器
glibc 2.21 实现
可以在下面找到 sysdeps/posix/timespec_get.c
作为:
int
timespec_get (struct timespec *ts, int base)
{
switch (base)
{
case TIME_UTC:
if (__clock_gettime (CLOCK_REALTIME, ts) < 0)
return 0;
break;
default:
return 0;
}
return base;
}
很清楚:
仅有的
TIME_UTC
目前支持它转发到
__clock_gettime (CLOCK_REALTIME, ts)
, ,这是一个 POSIX API: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.htmlLinux x86-64 有一个
clock_gettime
系统调用。请注意,这不是一种万无一失的微基准测试方法,因为:
man clock_gettime
表示如果您在程序运行时更改某些系统时间设置,则此测量可能会出现中断。当然,这应该是一个罕见的事件,你也许可以忽略它。这测量了挂机时间,因此如果调度程序决定忘记您的任务,它看起来会运行更长时间。
由于这些原因
getrusage()
可能是更好的 POSIX 基准测试工具,尽管它的微秒最大精度较低。更多信息请访问: 在 Linux 中测量时间 - time、clock、getrusage、clock_gettime、gettimeofday、timespec_get?
您所能得到的最佳精度是通过使用x86的唯一“RDTSC”指令,它可以提供时钟级分辨率(当然NE必须考虑到的成本RDTSC调用本身,它可以是在应用程序启动容易地测量)。
这里的主要捕捉正在测量每秒的时钟数,其不应太硬。
接受的答案是好的enough.But我的解决方案更simple.I在Linux下使用GCC(Ubuntu的7.2.0-8ubuntu3.2)7.2.0。
刚刚测试 ALSE使用gettimeofday
,所述tv_sec
是第二部分,和所述tv_usec
是的微秒下,不<强>毫秒强>
long currentTimeMillis() {
struct timeval time;
gettimeofday(&time, NULL);
return time.tv_sec * 1000 + time.tv_usec / 1000;
}
int main() {
printf("%ld\n", currentTimeMillis());
// wait 1 second
sleep(1);
printf("%ld\n", currentTimeMillis());
return 0;
}
有打印:
1522139691342
1522139692342
,恰好一个第二
在窗口:
SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);