C语言 Mac OS X 中的clock_gettime 替代方案

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5167269/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:56:31  来源:igfitidea点击:

clock_gettime alternative in Mac OS X

cmacostimeclock

提问by Delan Azabani

When compiling a program I wrote on Mac OS X after installing the necessary libraries through MacPorts, I get this error:

在通过 MacPorts 安装必要的库后编译我在 Mac OS X 上编写的程序时,出现此错误:

In function 'nanotime':
error: 'CLOCK_REALTIME' undeclared (first use in this function)
error: (Each undeclared identifier is reported only once
error: for each function it appears in.)

It appears that clock_gettimeis not implemented in Mac OS X. Is there an alternative means of getting the epoch timein nanoseconds? Unfortunately gettimeofdayis in microseconds.

似乎clock_gettime没有在 Mac OS X 中实现。是否有另一种方法可以以纳秒为单位获取纪元时间?不幸的是在微秒gettimeofday

采纳答案by Jens Gustedt

In effect, it seems not to be implemented for macOS before Sierra 10.12. You may want to look at this blog entry, but this doesn't seem to be available anymore. The main idea is in the following code snippet:

实际上,在 Sierra 10.12 之前的 macOS 似乎没有实现它。您可能想查看此博客条目,但这似乎不再可用。主要思想在以下代码片段中:

#include <mach/mach_time.h>
#define ORWL_NANO (+1.0E-9)
#define ORWL_GIGA UINT64_C(1000000000)

static double orwl_timebase = 0.0;
static uint64_t orwl_timestart = 0;

struct timespec orwl_gettime(void) {
  // be more careful in a multithreaded environement
  if (!orwl_timestart) {
    mach_timebase_info_data_t tb = { 0 };
    mach_timebase_info(&tb);
    orwl_timebase = tb.numer;
    orwl_timebase /= tb.denom;
    orwl_timestart = mach_absolute_time();
  }
  struct timespec t;
  double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase;
  t.tv_sec = diff * ORWL_NANO;
  t.tv_nsec = diff - (t.tv_sec * ORWL_GIGA);
  return t;
}

回答by jbenet

After hours of perusing different answers, blogs, and headers, I found a portable way to get the current time:

经过数小时的阅读不同的答案、博客和标题,我找到了一种获取当前时间的便携方法:

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

#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif



struct timespec ts;

#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
ts.tv_sec = mts.tv_sec;
ts.tv_nsec = mts.tv_nsec;

#else
clock_gettime(CLOCK_REALTIME, &ts);
#endif

or check out this gist: https://gist.github.com/1087739

或查看此要点:https: //gist.github.com/1087739

Hope this saves someone time. Cheers!

希望这可以节省某人的时间。干杯!

回答by Sergey D

None of the solutions above answers the question. Either they don't give you absolute Unix time, or their accuracy is 1 microsecond. The most popular solution by jbenet is slow (~6000ns) and does not count in nanoseconds even though its return suggests so. Below is a test for 2 solutions suggested by jbenet and Dmitri B, plus my take on this. You can run the code without changes.

上述解决方案都没有回答这个问题。要么他们没有给你绝对的 Unix 时间,要么他们的精度是 1 微秒。jbenet 最流行的解决方案很慢(~6000ns),即使它的返回表明是这样,也不以纳秒计。下面是对 jbenet 和 Dmitri B 建议的 2 个解决方案的测试,以及我对此的看法。您无需更改即可运行代码。

The 3rd solution does count in nanoseconds and gives you absolute Unix time reasonably fast (~90ns). So if someone find it useful - please let us all know here :-). I will stick to the one from Dmitri B (solution #1 in the code) - it fits my needs better.

第三个解决方案确实以纳秒为单位计算,并为您提供相当快的绝对 Unix 时间(~90ns)。所以如果有人觉得它有用 - 请在这里告诉我们:-)。我会坚持使用 Dmitri B 中的那个(代码中的解决方案 #1)——它更符合我的需求。

I needed commercial quality alternative to clock_gettime() to make pthread_…timed.. calls, and found this discussion very helpful. Thanks guys.

我需要clock_gettime() 的商业质量替代品来进行pthread_…timed.. 调用,并且发现这个讨论非常有帮助。谢谢你们。

/*
 Ratings of alternatives to clock_gettime() to use with pthread timed waits:
    Solution 1 "gettimeofday":
        Complexity      : simple
        Portability     : POSIX 1
        timespec        : easy to convert from timeval to timespec
        granularity     : 1000 ns,
        call            : 120 ns,
        Rating          : the best.

    Solution 2 "host_get_clock_service, clock_get_time":
        Complexity      : simple (error handling?)
        Portability     : Mac specific (is it always available?)
        timespec        : yes (struct timespec return)
        granularity     : 1000 ns (don't be fooled by timespec format)
        call time       : 6000 ns
        Rating          : the worst.

    Solution 3 "mach_absolute_time + gettimeofday once":
        Complexity      : simple..average (requires initialisation)
        Portability     : Mac specific. Always available
        timespec        : system clock can be converted to timespec without float-math
        granularity     : 1 ns.
        call time       : 90 ns unoptimised.
        Rating          : not bad, but do we really need nanoseconds timeout?

 References:
 - OS X is UNIX System 3 [U03] certified
    http://www.opengroup.org/homepage-items/c987.html

 - UNIX System 3 <--> POSIX 1 <--> IEEE Std 1003.1-1988
    http://en.wikipedia.org/wiki/POSIX
    http://www.unix.org/version3/

 - gettimeofday() is mandatory on U03,
   clock_..() functions are optional on U03,
   clock_..() are part of POSIX Realtime extensions
    http://www.unix.org/version3/inttables.pdf

 - clock_gettime() is not available on MacMini OS X
    (Xcode > Preferences > Downloads > Command Line Tools = Installed)

 - OS X recommends to use gettimeofday to calculate values for timespec
    https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/pthread_cond_timedwait.3.html

 - timeval holds microseconds, timespec - nanoseconds
    http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html

 - microtime() is used by kernel to implement gettimeofday()
    http://ftp.tw.freebsd.org/pub/branches/7.0-stable/src/sys/kern/kern_time.c

 - mach_absolute_time() is really fast
    http://www.opensource.apple.com/source/Libc/Libc-320.1.3/i386/mach/mach_absolute_time.c

 - Only 9 deciaml digits have meaning when int nanoseconds converted to double seconds
    Tutorial: Performance and Time post uses .12 precision for nanoseconds
    http://www.macresearch.org/tutorial_performance_and_time

 Example:
    Three ways to prepare absolute time 1500 milliseconds in the future to use with pthread timed functions.

 Output, N = 3, stock MacMini, OSX 10.7.5, 2.3GHz i5, 2GB 1333MHz DDR3:
    inittime.tv_sec = 1390659993
    inittime.tv_nsec = 361539000
    initclock = 76672695144136
    get_abs_future_time_0() : 1390659994.861599000
    get_abs_future_time_0() : 1390659994.861599000
    get_abs_future_time_0() : 1390659994.861599000
    get_abs_future_time_1() : 1390659994.861618000
    get_abs_future_time_1() : 1390659994.861634000
    get_abs_future_time_1() : 1390659994.861642000
    get_abs_future_time_2() : 1390659994.861643671
    get_abs_future_time_2() : 1390659994.861643877
    get_abs_future_time_2() : 1390659994.861643972
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>       /* gettimeofday */
#include <mach/mach_time.h> /* mach_absolute_time */
#include <mach/mach.h>      /* host_get_clock_service, mach_... */
#include <mach/clock.h>     /* clock_get_time */

#define BILLION 1000000000L
#define MILLION 1000000L

#define NORMALISE_TIMESPEC( ts, uint_milli )            \
    do {                                                \
        ts.tv_sec += uint_milli / 1000u;                \
        ts.tv_nsec += (uint_milli % 1000u) * MILLION;   \
        ts.tv_sec += ts.tv_nsec / BILLION;              \
        ts.tv_nsec = ts.tv_nsec % BILLION;              \
    } while (0)

static mach_timebase_info_data_t timebase = { 0, 0 }; /* numer = 0, denom = 0 */
static struct timespec           inittime = { 0, 0 }; /* nanoseconds since 1-Jan-1970 to init() */
static uint64_t                  initclock;           /* ticks since boot to init() */

void init()
{
    struct timeval  micro;      /* microseconds since 1 Jan 1970 */

    if (mach_timebase_info(&timebase) != 0)
        abort();                            /* very unlikely error */

    if (gettimeofday(&micro, NULL) != 0)
        abort();                            /* very unlikely error */

    initclock = mach_absolute_time();

    inittime.tv_sec = micro.tv_sec;
    inittime.tv_nsec = micro.tv_usec * 1000;
    printf("\tinittime.tv_sec = %ld\n", inittime.tv_sec);
    printf("\tinittime.tv_nsec = %ld\n", inittime.tv_nsec);
    printf("\tinitclock = %ld\n", (long)initclock);
}

/*
 * Get absolute future time for pthread timed calls
 *  Solution 1: microseconds granularity
 */
struct timespec get_abs_future_time_coarse(unsigned milli)
{
    struct timespec future;         /* ns since 1 Jan 1970 to 1500 ms in the future */
    struct timeval  micro = {0, 0}; /* 1 Jan 1970 */

    (void) gettimeofday(&micro, NULL);
    future.tv_sec = micro.tv_sec;
    future.tv_nsec = micro.tv_usec * 1000;
    NORMALISE_TIMESPEC( future, milli );
    return future;
}

/*
 * Solution 2: via clock service
 */
struct timespec get_abs_future_time_served(unsigned milli)
{
    struct timespec     future;
    clock_serv_t        cclock;
    mach_timespec_t     mts;

    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
    clock_get_time(cclock, &mts);
    mach_port_deallocate(mach_task_self(), cclock);
    future.tv_sec = mts.tv_sec;
    future.tv_nsec = mts.tv_nsec;
    NORMALISE_TIMESPEC( future, milli );
    return future;
}

/*
 * Solution 3: nanosecond granularity
 */
struct timespec get_abs_future_time_fine(unsigned milli)
{
    struct timespec future;     /* ns since 1 Jan 1970 to 1500 ms in future */
    uint64_t        clock;      /* ticks since init */
    uint64_t        nano;       /* nanoseconds since init */

    clock = mach_absolute_time() - initclock;
    nano = clock * (uint64_t)timebase.numer / (uint64_t)timebase.denom;
    future = inittime;
    future.tv_sec += nano / BILLION;
    future.tv_nsec += nano % BILLION;
    NORMALISE_TIMESPEC( future, milli );
    return future;
}

#define N 3

int main()
{
    int                 i, j;
    struct timespec     time[3][N];
    struct timespec   (*get_abs_future_time[])(unsigned milli) =
    {
        &get_abs_future_time_coarse,
        &get_abs_future_time_served,
        &get_abs_future_time_fine
    };

    init();
    for (j = 0; j < 3; j++)
        for (i = 0; i < N; i++)
            time[j][i] = get_abs_future_time[j](1500);  /* now() + 1500 ms */

    for (j = 0; j < 3; j++)
        for (i = 0; i < N; i++)
            printf("get_abs_future_time_%d() : %10ld.%09ld\n",
                   j, time[j][i].tv_sec, time[j][i].tv_nsec);

    return 0;
}

回答by Dmitri Bouianov

#if defined(__MACH__) && !defined(CLOCK_REALTIME)
#include <sys/time.h>
#define CLOCK_REALTIME 0
// clock_gettime is not implemented on older versions of OS X (< 10.12).
// If implemented, CLOCK_REALTIME will have already been defined.
int clock_gettime(int /*clk_id*/, struct timespec* t) {
    struct timeval now;
    int rv = gettimeofday(&now, NULL);
    if (rv) return rv;
    t->tv_sec  = now.tv_sec;
    t->tv_nsec = now.tv_usec * 1000;
    return 0;
}
#endif

回答by Charphacy

Everything you need is described in Technical Q&A QA1398: Technical Q&A QA1398: Mach Absolute Time Units, basically the function you want is mach_absolute_time.

您需要的一切都在技术问答 QA1398:技术问答 QA1398:马赫绝对时间单位 中描述,基本上您想要的功能是mach_absolute_time

Here's a slightly earlier version of the sample code from that page that does everything using Mach calls (the current version uses AbsoluteToNanosecondsfrom CoreServices). In current OS X (i.e., on Snow Leopard on x86_64) the absolute time values are actually in nanoseconds and so don't actually require any conversion at all. So, if you're good and writing portable code, you'll convert, but if you're just doing something quick and dirty for yourself, you needn't bother.

这是来自该页面的示例代码的一个稍早版本,它使用 Mach 调用完成所有操作(当前版本使用AbsoluteToNanoseconds来自 CoreServices)。在当前的 OS X(即 x86_64 上的 Snow Leopard)中,绝对时间值实际上以纳秒为单位,因此实际上根本不需要任何转换。所以,如果你擅长编写可移植的代码,你就会转换,但如果你只是为自己做一些快速而肮脏的事情,你就不必费心了。

FWIW, mach_absolute_timeis reallyfast.

FWIW,mach_absolute_time真的快。

uint64_t GetPIDTimeInNanoseconds(void)
{
    uint64_t        start;
    uint64_t        end;
    uint64_t        elapsed;
    uint64_t        elapsedNano;
    static mach_timebase_info_data_t    sTimebaseInfo;

    // Start the clock.

    start = mach_absolute_time();

    // Call getpid. This will produce inaccurate results because 
    // we're only making a single system call. For more accurate 
    // results you should call getpid multiple times and average 
    // the results.

    (void) getpid();

    // Stop the clock.

    end = mach_absolute_time();

    // Calculate the duration.

    elapsed = end - start;

    // Convert to nanoseconds.

    // If this is the first time we've run, get the timebase.
    // We can use denom == 0 to indicate that sTimebaseInfo is 
    // uninitialised because it makes no sense to have a zero 
    // denominator is a fraction.

    if ( sTimebaseInfo.denom == 0 ) {
        (void) mach_timebase_info(&sTimebaseInfo);
    }

    // Do the maths. We hope that the multiplication doesn't 
    // overflow; the price you pay for working in fixed point.

    elapsedNano = elapsed * sTimebaseInfo.numer / sTimebaseInfo.denom;

    printf("multiplier %u / %u\n", sTimebaseInfo.numer, sTimebaseInfo.denom);
    return elapsedNano;
}

回答by James Wald

Note that macOS Sierra 10.12 now supports clock_gettime():

请注意,macOS Sierra 10.12 现在支持 clock_gettime():

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

int main() {
    struct timespec res;
    struct timespec time;

    clock_getres(CLOCK_REALTIME, &res);
    clock_gettime(CLOCK_REALTIME, &time);

    printf("CLOCK_REALTIME: res.tv_sec=%lu res.tv_nsec=%lu\n", res.tv_sec, res.tv_nsec);
    printf("CLOCK_REALTIME: time.tv_sec=%lu time.tv_nsec=%lu\n", time.tv_sec, time.tv_nsec);
}

It does provide nanoseconds; however, the resolution is 1000, so it is (in)effectively limited to microseconds:

它确实提供了纳秒;然而,分辨率是 1000,所以它(in)有效地限制在微秒内:

CLOCK_REALTIME: res.tv_sec=0 res.tv_nsec=1000
CLOCK_REALTIME: time.tv_sec=1475279260 time.tv_nsec=525627000

You will need XCode 8 or later to be able to use this feature. Code compiled to use this feature will not run on versions of Mac OS X (10.11 or earlier).

您将需要 XCode 8 或更高版本才能使用此功能。为使用此功能而编译的代码将无法在 Mac OS X 版本(10.11 或更早版本)上运行。

回答by ilciavo

Thanks for your posts

谢谢你的帖子

I think you can add the following lines

我想你可以添加以下几行

#ifdef __MACH__
#include <mach/mach_time.h>
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 0
int clock_gettime(int clk_id, struct timespec *t){
    mach_timebase_info_data_t timebase;
    mach_timebase_info(&timebase);
    uint64_t time;
    time = mach_absolute_time();
    double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom);
    double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9);
    t->tv_sec = seconds;
    t->tv_nsec = nseconds;
    return 0;
}
#else
#include <time.h>
#endif

Let me know what you get for latency and granularity

让我知道你得到的延迟和粒度

回答by P Marecki

Maristic has the best answer here to date. Let me simplify and add a remark. #includeand Init():

迄今为止,Maristic 在这里给出了最好的答案。让我简化一下并补充一点。#includeInit()

#include <mach/mach_time.h>

double conversion_factor;

void Init() {
  mach_timebase_info_data_t timebase;
  mach_timebase_info(&timebase);
  conversion_factor = (double)timebase.numer / (double)timebase.denom;
}

Use as:

用于:

  uint64_t t1, t2;

  Init();

  t1 = mach_absolute_time();
  /* profiled code here */
  t2 = mach_absolute_time();

  double duration_ns = (double)(t2 - t1) * conversion_factor;  

Such timer has latency of 65ns +/- 2ns(2GHz CPU). Use this if you need "time evolution" of single execution. Otherwise loop your code 10000times and profile even with gettimeofday(), which is portable (POSIX), and has the latency of 100ns +/- 0.5ns(though only 1usgranularity).

这种计时器的延迟为65ns +/- 2ns(2GHz CPU)。如果您需要单次执行的“时间演变”,请使用此选项。否则循环你的代码10000时间和配置文件,即使gettimeofday()是可移植的(POSIX),并且具有100ns +/- 0.5ns(尽管只有1us粒度)的延迟。

回答by Bernd Paysan

I tried the version with clock_get_time, and did cache the host_get_clock_service call. It's way slower than gettimeofday, it takes several microseconds per invocation. And, what's worse, the return value has steps of 1000, i.e. it's still microsecond granularity.

我尝试了带有clock_get_time 的版本,并缓存了host_get_clock_service 调用。它比 gettimeofday 慢得多,每次调用需要几微秒。而且,更糟糕的是,返回值的步长为 1000,即它仍然是微秒级粒度。

I'd advice to use gettimeofday, and multiply tv_usec by 1000.

我建议使用 gettimeofday,并将 tv_usec 乘以 1000。

回答by ChisholmKyle

Based on the open source mach_absolute_time.cwe can see that the line extern mach_port_t clock_port;tells us there's a mach port already initialized for monotonic time. This clock port can be accessed directly without having to resort to calling mach_absolute_timethen converting backto a struct timespec. Bypassing a call to mach_absolute_timeshould improve performance.

基于开源的mach_absolute_time.c,我们可以看到该行extern mach_port_t clock_port;告诉我们已经为单调时间初始化了一个 mach 端口。这个时钟端口可以直接而不必诉诸于调用来访问mach_absolute_time然后转换struct timespec。绕过调用mach_absolute_time应该可以提高性能。

I created a small Github repo (PosixMachTiming)with the code based on the extern clock_portand a similar thread. PosixMachTimingemulates clock_gettimefor CLOCK_REALTIMEand CLOCK_MONOTONIC. It also emulates the function clock_nanosleepfor absolute monotonic time. Please give it a try and see how the performance compares. Maybe you might want to create comparative tests or emulate other POSIX clocks/functions?

我使用基于 extern和类似线程的代码创建了一个小型Github 存储库(PosixMachTiming)PosixMachTiming模拟的和。它还模拟绝对单调时间的函数。请尝试一下,看看性能比较如何。也许您可能想要创建比较测试或模拟其他 POSIX 时钟/功能?clock_portclock_gettimeCLOCK_REALTIMECLOCK_MONOTONICclock_nanosleep