在C中获得以微秒为单位的时间戳?

1yjd4xko  于 2023-01-29  发布在  其他
关注(0)|答案(9)|浏览(246)

如何在C中获得微秒时间戳?
我在努力做到:

struct timeval tv;
gettimeofday(&tv,NULL);
return tv.tv_usec;

但是这会返回一些无意义的值,如果我得到两个时间戳,第二个可以比第一个更小或更大(第二个应该总是更大)。有没有可能把gettimeofday返回的幻整数转换成一个实际上可以处理的正常数字?

w80xi6nr

w80xi6nr1#

您还需要添加秒数:

unsigned long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;

请注意,这只会持续大约232/106 =~ 4295秒,或者大约71分钟(在典型的32位系统上)。

rqqzpn5f

rqqzpn5f2#

获取微秒时间戳有两种选择,第一种(也是最好的)选择是直接使用timeval类型:

struct timeval GetTimeStamp() {
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return tv;
}

第二种选择(对我来说不太理想)是从timeval构建uint64_t:

uint64_t GetTimeStamp() {
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return tv.tv_sec*(uint64_t)1000000+tv.tv_usec;
}
oewdyzsn

oewdyzsn3#

在C中获得以微秒为单位的时间戳?
以下是与此问题的 * 标题 * 相关的一般答案:

如何在C中获取简单的时间戳

1.对于函数millis()以毫秒(ms)为单位,
1.微秒(us),micros(),以及
1.纳秒(ns),nanos()

  • 快速总结:如果你很着急,并且使用Linux或POSIX系统,直接跳到下面的"millis()micros(),和nanos()"部分,然后使用这些函数。如果你在Linux或POSIX系统上使用C11而不是,你需要用timespec_get()替换这些函数中的clock_gettime()。*

C语言中的2个主要时间戳函数:

1.* * C11:****timespec_get()是 * C11 * 或更高版本标准的一部分,但不允许选择要使用的时钟类型。它也适用于C17。请参见documentation for std::timespec_get() here。但是,对于C11及更高版本,我更喜欢使用一种不同的方法,我可以 * 指定分辨率 * 和时钟类型,如我在此处的答案中所演示的:在C中获得精确的执行时间(微秒)。
C11 timespec_get()解决方案比C
解决方案的局限性稍大一些,因为您不能指定时钟分辨率或单调性("单调"时钟定义为只能向前计数并且 * 永远 * 不能向后运行或跳转的时钟--例如:当测量时间差时,单调时钟是理想的,以确保您永远不会将时钟校正跳变计数为您"测量"时间的一部分。
因此,由于我们无法指定要使用的时钟,timespec_get()返回的时间戳值的分辨率可能取决于您的 * 硬件架构 操作系统 * 和 * 编译器 。该函数分辨率的 * 近似值 * 可以通过快速连续地进行1000次左右的测量来获得。然后找到任意两个后续测量值之间的最小差值。您的时钟的实际 * 分辨率 * 保证 * 等于或小于 * 该最小差值。
我在我的timinglib.c定时库的get_estimated_resolution()函数中演示了这一点。
1.
* Linux和POSIX:****比C
中的timespec_get()更好的是Linux and POSIX function clock_gettime()函数,它在Linux或POSIX系统上的C++中也能很好地工作。clock_gettime()**允许您选择所需的时钟。您可以使用clock_getres()读取指定的时钟分辨率,尽管这也不会给您硬件的真实时钟分辨率。相反,它给您struct timespectv_nsec成员的单位。使用我上面描述的get_estimated_resolution()函数和我的timinglib.c/. h文件来获得分辨率的估计值。

    • 因此,如果您在Linux或POSIX系统上使用C,我 * 强烈建议 * 您使用clock_gettime()而不是timespec_get()。**

C11的timespec_get()(不错)和Linux/POSIX的clock_gettime()(更好):

以下是如何使用这两个函数:
1.* * C11的timespec_get()**

  1. https://en.cppreference.com/w/c/chrono/timespec_get
    1.在C中工作,但不允许您选择要使用的时钟。
    1.* * 完整示例,带错误检查:**
#include <stdint.h> // `UINT64_MAX`
#include <stdio.h>  // `printf()`
#include <time.h>   // `timespec_get()`

/// Convert seconds to nanoseconds
#define SEC_TO_NS(sec) ((sec)*1000000000)

uint64_t nanoseconds;
struct timespec ts;
int return_code = timespec_get(&ts, TIME_UTC);
if (return_code == 0)
{
    printf("Failed to obtain timestamp.\n");
    nanoseconds = UINT64_MAX; // use this to indicate error
}
else
{
    // `ts` now contains your timestamp in seconds and nanoseconds! To 
    // convert the whole struct to nanoseconds, do this:
    nanoseconds = SEC_TO_NS((uint64_t)ts.tv_sec) + (uint64_t)ts.tv_nsec;
}

1.* * Linux/POSIX的clock_gettime()**--* 尽可能使用此版本! *

  1. https://man7.org/linux/man-pages/man3/clock_gettime.3.html(此函数的最佳参考)和:
  2. https://linux.die.net/man/3/clock_gettime
    1.在Linux或POSIX系统上用C语言工作,并允许您选择要使用的时钟!
    1.我选择了CLOCK_MONOTONIC_RAW时钟,它最适合于获取系统上用于计时的时间戳。
    1.请参阅此处所有时钟类型的定义,例如CLOCK_REALTIMECLOCK_MONOTONICCLOCK_MONOTONIC_RAW等:https://man7.org/linux/man-pages/man3/clock_gettime.3.html
    1.另一个流行的时钟是CLOCK_REALTIME。但是,不要混淆!"实时"并不意味着它是一个用于"实时"操作系统或精确计时的好时钟。相反,它意味着如果时钟漂移,它是一个将定期调整到"实时"或实际"世界时间"的时钟。同样,不要将此时钟用于精确计时用途,因为它可以在任何时候由系统向前或向后调整,不受您的控制。
    1.* * 完整示例,带错误检查:**
// This line **must** come **before** including <time.h> in order to
// bring in the POSIX functions such as `clock_gettime() from <time.h>`!
#define _POSIX_C_SOURCE 199309L

#include <errno.h>  // `errno`
#include <stdint.h> // `UINT64_MAX`
#include <stdio.h>  // `printf()`
#include <string.h> // `strerror(errno)`
#include <time.h>   // `clock_gettime()` and `timespec_get()`

/// Convert seconds to nanoseconds
#define SEC_TO_NS(sec) ((sec)*1000000000)

uint64_t nanoseconds;
struct timespec ts;
int return_code = clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
if (return_code == -1)
{
    printf("Failed to obtain timestamp. errno = %i: %s\n", errno, 
        strerror(errno));
    nanoseconds = UINT64_MAX; // use this to indicate error
}
else
{
    // `ts` now contains your timestamp in seconds and nanoseconds! To 
    // convert the whole struct to nanoseconds, do this:
    nanoseconds = SEC_TO_NS((uint64_t)ts.tv_sec) + (uint64_t)ts.tv_nsec;
}

millis()micros()nanos()

无论如何,下面是我在C语言中使用的millis()micros()nanos()函数,用于简单的时间戳和代码速度分析。
我正在使用下面的Linux/POSIX clock_gettime()函数。如果您在 * 没有 * clock_gettime()可用的系统上使用C11或更高版本,只需将下面所有clock_gettime(CLOCK_MONOTONIC_RAW, &ts)的用法替换为timespec_get(&ts, TIME_UTC)即可。
从我的eRCaGuy_hello_world repo获取我的代码的最新版本:

  1. timinglib.h
  2. timinglib.c
// This line **must** come **before** including <time.h> in order to
// bring in the POSIX functions such as `clock_gettime() from <time.h>`!
#define _POSIX_C_SOURCE 199309L
        
#include <time.h>

/// Convert seconds to milliseconds
#define SEC_TO_MS(sec) ((sec)*1000)
/// Convert seconds to microseconds
#define SEC_TO_US(sec) ((sec)*1000000)
/// Convert seconds to nanoseconds
#define SEC_TO_NS(sec) ((sec)*1000000000)

/// Convert nanoseconds to seconds
#define NS_TO_SEC(ns)   ((ns)/1000000000)
/// Convert nanoseconds to milliseconds
#define NS_TO_MS(ns)    ((ns)/1000000)
/// Convert nanoseconds to microseconds
#define NS_TO_US(ns)    ((ns)/1000)

/// Get a time stamp in milliseconds.
uint64_t millis()
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
    uint64_t ms = SEC_TO_MS((uint64_t)ts.tv_sec) + NS_TO_MS((uint64_t)ts.tv_nsec);
    return ms;
}

/// Get a time stamp in microseconds.
uint64_t micros()
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
    uint64_t us = SEC_TO_US((uint64_t)ts.tv_sec) + NS_TO_US((uint64_t)ts.tv_nsec);
    return us;
}

/// Get a time stamp in nanoseconds.
uint64_t nanos()
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
    uint64_t ns = SEC_TO_NS((uint64_t)ts.tv_sec) + (uint64_t)ts.tv_nsec;
    return ns;
}

// NB: for all 3 timestamp functions above: gcc defines the type of the internal
// `tv_sec` seconds value inside the `struct timespec`, which is used
// internally in these functions, as a signed `long int`. For architectures
// where `long int` is 64 bits, that means it will have undefined
// (signed) overflow in 2^64 sec = 5.8455 x 10^11 years. For architectures
// where this type is 32 bits, it will occur in 2^32 sec = 136 years. If the
// implementation-defined epoch for the timespec is 1970, then your program
// could have undefined behavior signed time rollover in as little as
// 136 years - (year 2021 - year 1970) = 136 - 51 = 85 years. If the epoch
// was 1900 then it could be as short as 136 - (2021 - 1900) = 136 - 121 =
// 15 years. Hopefully your program won't need to run that long. :). To see,
// by inspection, what your system's epoch is, simply print out a timestamp and
// calculate how far back a timestamp of 0 would have occurred. Ex: convert
// the timestamp to years and subtract that number of years from the present
// year.

时间戳分辨率:

在我使用gcc编译器的x86 - 64 Linux Ubuntu 18.04系统上,clock_getres()返回1 ns的分辨率。

对于clock_gettime()timespec_get(),我还进行了经验测试,其中我尽可能快地获取了1000个时间戳(参见我的timinglib.c定时库的get_estimated_resolution()函数),并查看时间戳样本之间的最小间隔。这表明范围为**~14~26 ns在我的系统上使用timespec_get(&ts, TIME_UTC)clock_gettime(CLOCK_MONOTONIC, &ts)时,~75~130 ns**(对于clock_gettime(CLOCK_MONOTONIC_RAW, &ts))。这可视为这些函数的粗略"实际分辨率"。请参见timinglib_get_resolution.c中的测试代码,并参见timinglib.c中我的get_estimated_resolution()get_specified_resolution()函数(该测试代码使用的函数)的定义。

  • 这些结果因硬件而异,您的硬件结果可能有所不同。*

参考文献:

1.我在上面链接到的www.example.com文档源。 cppreference.com documentation sources I link to above.

  1. This answer here by @Ciro Santilli新疆棉花
    1.[我的答案] my answer about usleep() and nanosleep()-它提醒我需要做#define _POSIX_C_SOURCE 199309L才能从<time.h>引入clock_gettime() POSIX函数!
  2. https://linux.die.net/man/3/clock_gettime
  3. https://man7.org/linux/man-pages/man3/clock_gettime.3.html
    1.提及以下要求:
    _POSIX_C_SOURCE >= 199309L
    1.请参阅这里所有时钟类型的定义,例如CLOCK_REALTIMECLOCK_MONOTONICCLOCK_MONOTONIC_RAW等。

另请参见:

1.我的答案比较简短,只适用于ANSI/ISO C11或更高版本:How to measure time in milliseconds using ANSI C?
1.我的3组时间戳函数(相互交叉链接):
1.对于C时间戳,请参见我的答案:Get a timestamp in C in microseconds?
1.对于C++高分辨率时间戳,请参见我的答案:Here is how to get simple C-like millisecond, microsecond, and nanosecond timestamps in C++
1.对于Python高分辨率时间戳,请参见我的答案:How can I get millisecond and microsecond-resolution timestamps in Python?

  1. https://en.cppreference.com/w/c/chrono/clock
  2. POSIX clock_gettime()https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
  3. Linux上的clock_gettime()https://linux.die.net/man/3/clock_gettime
    1.注意:对于C11和更高版本,您 * 可以 * 使用timespec_get(),就像我在上面所做的那样,而不是POSIX clock_gettime()https://en.cppreference.com/w/c/chrono/clock表示:
    在C11中使用时间规格_get
    1.但是,使用clock_gettime()可以让您为所需的 * 时钟类型 * 选择所需的时钟ID!***

待办事项:

  1. 2022年4月3日完成:由于timespec_getres()在C23之前不受支持,因此请更新我的示例,使其包含一个在Linux上使用POSIX clock_gettime()clock_getres()函数的示例。我希望准确地了解在给定系统上我可以期望的时钟分辨率有多高。**是ms分辨率、us分辨率、ns分辨率还是其他分辨率?**有关参考,请参见:
  2. https://linux.die.net/man/3/clock_gettime
  3. https://people.cs.rutgers.edu/~pxk/416/notes/c-tutorials/gettime.html
  4. https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
    1.答案:clock_getres()返回1 ns,但实际分辨率大约为14~27 ns,根据我的get_estimated_resolution()函数:https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/c/timinglib.c。在此处查看结果:
  5. www.example.com https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/c/timinglib_get_resolution.c#L46-L77
    1.激活Linux SCHED_RR软实时循环调度程序,以获得最佳和最一致的定时。How to configure the Linux SCHED_RR soft real-time round-robin scheduler so that clock_nanosleep() can have improved resolution of ~4 us down from ~ 55 us.
u4dcyp6a

u4dcyp6a4#

struct timeval包含两个组件,秒和微秒。微秒精度的时间戳表示为从存储在tv_sec字段中的纪元开始的秒数和tv_usec中的小数微秒数。因此,您不能忽略tv_sec并期望得到合理的结果。
如果使用Linux或 *BSD,可以使用timersub()减去两个struct timeval值,这可能是您想要的。

bvjxkvbb

bvjxkvbb5#

来自C11的一个月一个月

返回精度最高为纳秒的值,舍入到实现的分辨率。

#include <time.h>
struct timespec ts;
timespec_get(&ts, TIME_UTC);
struct timespec {
    time_t   tv_sec;        /* seconds */
    long     tv_nsec;       /* nanoseconds */
};

更多细节请参见我的另一个答案:How to measure time in milliseconds using ANSI C?

bpzcxfmw

bpzcxfmw6#

但这会返回一些无意义的值,即如果我得到两个时间戳,则第二个时间戳可以比第一个时间戳小或大(第二个时间戳应该总是更大)。
你怎么会这么想呢?这个值可能没问题,这和秒和分的情况是一样的--当你用分和秒来测量时间时,当秒数达到60秒时,秒数就会滚到零。
要将返回值转换为“线性”数字,可以将秒数相乘并加上微秒,但如果我计算正确,一年大约是1 e6 606024360微秒,这意味着需要超过32位来存储结果:

$ perl -E '$_=1e6*60*60*24*360; say int log($_)/log(2)'
44

这可能是将原始返回值拆分为两部分的原因之一。

5cnsuln7

5cnsuln77#

使用unsigned long long(即64位单元)表示系统时间:

typedef unsigned long long u64;

u64 u64useconds;
struct timeval tv;

gettimeofday(&tv,NULL);
u64useconds = (1000000*tv.tv_sec) + tv.tv_usec;
8yparm6h

8yparm6h8#

迟做总比不做好!这个小程序可以作为获取时间戳(以微秒为单位)和计算进程时间(以微秒为单位)的最快方法:

#include <sys/time.h>
#include <stdio.h>
#include <time.h>

struct timeval GetTimeStamp() 
{
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return tv;
}

int main()
{
    struct timeval tv= GetTimeStamp(); // Calculate time
    signed long time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec; // Store time in microseconds
    
    getchar(); // Replace this line with the process that you need to time

    printf("Elapsed time: %ld microsecons\n", (1000000 * GetTimeStamp().tv_sec + GetTimeStamp().tv_usec) - time_in_micros);
    
}

你可以用一个函数/进程来替换getchar()。最后,你可以把它存储在一个有符号的long中,而不是打印差值。这个程序在Windows 10中运行良好。

g2ieeal7

g2ieeal79#

首先,我们需要知道微秒的范围,即000_000到999_999(1000000微秒等于1秒)。tv.tv_usec将返回0到999999的值,而不是00000到99999,因此,当使用秒时,我们可能会得到2.1秒,而不是2。000001秒,因为当只讨论tv_usec时,000001本质上是1。如果插入

if(tv.tv_usec<10)
{
 printf("00000");
} 
else if(tv.tv_usec<100&&tv.tv_usec>9)// i.e. 2digits
{
 printf("0000");
}

等等...

相关问题