часы gettime альтернатива в Mac OS X



при компиляции программы, которую я написал на Mac OS X после установки необходимых библиотек через MacPorts, я получаю эту ошибку:



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.)


получается, что clock_gettime не реализован в Mac OS X. Есть ли альтернативные средства получения эпоха времени на наносекунд? К сожалению gettimeofday находится в микросекунд.

757   13  

13 ответов:

по сути, он, кажется, не будет реализован для macOS до Sierra 10.12. Вы можете посмотреть на это запись в блог, но это, кажется, больше не доступно. Основная идея заключается в следующем фрагменте кода:

#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;
}

после нескольких часов просмотра различных ответов, блогов и заголовков, я нашел портативный способ получить текущее время:

#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

или проверить эту суть: https://gist.github.com/1087739

надеюсь, это сэкономит кому-то время. Ура!

ни одно из решений выше отвечает на вопрос. Либо они не дают вам абсолютного времени Unix, либо их точность составляет 1 микросекунду. Самое популярное решение jbenet является медленным (~6000ns) и не учитывается в наносекундах, хотя его возврат предполагает это. Ниже приведен тест для 2 решений, предложенных jbenet и Дмитрием B, плюс мой взгляд на это. Вы можете запустить код без изменений.

3-е решение действительно считается в наносекундах и дает вам абсолютное время Unix разумно быстро (~90нс). Так что если кто-то найдет это полезным - пожалуйста, дайте нам знать и здесь :-). Я буду придерживаться одного из Дмитрия Б (Решение № 1 в коде) - он лучше соответствует моим потребностям.

Мне нужен коммерческий качественной альтернативой при успешном выполнении функции clock_gettime (), чтобы сделать pthread_...время.. звонит, и нашел это обсуждение очень полезным. Спасибо, ребята.

/*
 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;
}
#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

все, что вам нужно, описано в технические вопросы QA1398: технические вопросы QA1398: Маха абсолютных единицах времени, в основном функция, которую вы хотите mach_absolute_time.

вот немного более ранняя версия примера кода с этой страницы, которая делает все, используя вызовы Mach (текущая версия использует AbsoluteToNanoseconds от CoreServices). В текущей OS X (т. е. на Snow Leopard на x86_64) абсолютные значения времени фактически находятся в наносекундах и поэтому фактически не требуют каких-либо преобразования на всех. Итак, если Вы хороши и пишете портативный код, вы будете конвертировать, но если вы просто делаете что-то быстрое и грязное для себя, вам не нужно беспокоиться.

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;
}

обратите внимание, что в macOS Сьерра-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);
}

он обеспечивает наносекунды; однако разрешение равно 1000, поэтому оно (in)эффективно ограничено микросекундами:

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

вам понадобится XCode 8 или более поздней версии, чтобы иметь возможность использовать эту функцию. Код, скомпилированный для использования этой функции, не будет работать в версиях Mac OS X (10.11 или более ранних).

Спасибо за ваши посты

Я думаю, вы можете добавить следующие строки

#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

Дайте мне знать, что вы получаете для латентности и детализации

Maristic имеет лучший ответ здесь на сегодняшний день. Позвольте мне упростить и добавить замечание. #include и Init():

#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;
}

как использовать:

  uint64_t t1, t2;

  Init();

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

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

такой таймер имеет задержку 65ns +/- 2ns (процессор 2 ГГц). Используйте это, если вам нужна "эволюция времени" одного выполнения. В противном случае цикл ваш код 10000 раз и профиль, даже с gettimeofday(), который является портативным (POSIX), и имеет задержку 100ns +/- 0.5ns (правда, только 1us гранулярности).

Я попробовал версию с clock_get_time, и сделал кэширование вызова host_get_clock_service. Это намного медленнее, чем gettimeofday, это занимает несколько микросекунд на вызов. И, что еще хуже, возвращаемое значение имеет шаги 1000, т. е. это все еще микросекундная гранулярность.

Я бы посоветовал использовать gettimeofday и умножить tv_usec на 1000.

на основе открытого исходного кода mach_absolute_time.c мы видим, что строка extern mach_port_t clock_port; говорит нам, что порт mach уже инициализирован для монотонного времени. Этот порт часов можно получить напрямую, не прибегая к вызову mach_absolute_time после преобразования назад до struct timespec. Обход вызова на mach_absolute_time должны улучшить производительность.

Я создал небольшой GitHub repo (PosixMachTiming) С кодом на основе extern clock_port и похожие темы. PosixMachTiming эмулирует clock_gettime на CLOCK_REALTIME и CLOCK_MONOTONIC. Он также эмулирует функцию clock_nanosleep для абсолютного монотонного времени. Пожалуйста, дайте ему попробовать и посмотреть, как производительность сравнивает. Возможно, вы захотите создать сравнительные тесты или эмулировать другие часы/функции POSIX?

по крайней мере, с тех пор, как горный лев,mach_absolute_time() возвращает наносекунд и не абсолютное время (которое было числом циклов шины).

следующий код на моем MacBook Pro (2 ГГц Core i7) показал, что время для вызова mach_absolute_time() усреднил 39 НС за 10 запусков (мин 35, Макс 45), что в основном время между возвратом двух вызовов mach_absolute_time (), около 1 вызова:

#include <stdint.h>
#include <mach/mach_time.h>
#include <iostream>

using namespace std;

int main()
{
   uint64_t now, then;
   uint64_t abs;

   then = mach_absolute_time(); // return nanoseconds
   now = mach_absolute_time();
   abs = now - then;

   cout << "nanoseconds = " << abs << endl;
}

Я нашел другое портативное решение.

объявите в каком-нибудь заголовочном файле (или даже в исходном):

/* If compiled on DARWIN/Apple platforms. */
#ifdef DARWIN
#define    CLOCK_REALTIME    0x2d4e1588
#define    CLOCK_MONOTONIC   0x0
#endif /* DARWIN */

и добавить реализацию функции:

#ifdef DARWIN

/*
 * Bellow we provide an alternative for clock_gettime,
 * which is not implemented in Mac OS X.
 */
static inline int clock_gettime(int clock_id, struct timespec *ts)
{
    struct timeval tv;

    if (clock_id != CLOCK_REALTIME) 
    {
        errno = EINVAL;
        return -1;
    }
    if (gettimeofday(&tv, NULL) < 0) 
    {
        return -1;
    }
    ts->tv_sec = tv.tv_sec;
    ts->tv_nsec = tv.tv_usec * 1000;
    return 0;
}

#endif /* DARWIN */

не забудьте включить <time.h>.

void clock_get_uptime(uint64_t *result);

void clock_get_system_microtime(            uint32_t *secs,
                                            uint32_t *microsecs);

void clock_get_system_nanotime(             uint32_t *secs,
                                            uint32_t *nanosecs);
void clock_get_calendar_microtime(          uint32_t *secs,
                                            uint32_t *microsecs);

void clock_get_calendar_nanotime(           uint32_t *secs,
                                            uint32_t *nanosecs);

для MacOS вы можете найти хорошую информацию на их странице разработчиков https://developer.apple.com/library/content/documentation/Darwin/Conceptual/KernelProgramming/services/services.html

Comments

    Ничего не найдено.