Objective c OSX上的单调时钟

Objective c OSX上的单调时钟,objective-c,macos,posix,clock,timing,Objective C,Macos,Posix,Clock,Timing,CLOCK\u单调似乎不可用,因此CLOCK\u gettime已过期 我曾在一些地方读到mach_absolute_time()可能是正确的方法,但在读到它是一个“依赖cpu的值”后,我立即怀疑它是否在使用rtdsc。因此,即使该值是单调的,它也可能随时间漂移。此外,线程亲和性问题可能导致调用函数的结果有意义地不同(使函数在所有内核中都不是单调的) 当然,这只是猜测。有人知道马赫绝对时间是如何工作的吗?事实上,我正在寻找一个替代品来代替clock_gettime(clock_MONOTONIC

CLOCK\u单调似乎不可用,因此CLOCK\u gettime已过期

我曾在一些地方读到mach_absolute_time()可能是正确的方法,但在读到它是一个“依赖cpu的值”后,我立即怀疑它是否在使用rtdsc。因此,即使该值是单调的,它也可能随时间漂移。此外,线程亲和性问题可能导致调用函数的结果有意义地不同(使函数在所有内核中都不是单调的)

当然,这只是猜测。有人知道马赫绝对时间是如何工作的吗?事实上,我正在寻找一个替代品来代替clock_gettime(clock_MONOTONIC…或者OSX的类似产品。无论时钟源是什么,我希望至少有毫秒精度和毫秒精度

我只想了解哪些时钟可用,哪些时钟是单调的,如果某些时钟漂移,有线程关联问题,不是所有Mac硬件都支持,或者需要“超高”的cpu周期来执行

以下是我能够找到的关于这个主题的链接(有些已经是死链接,无法在archive.org上找到):

http://www.wand.net.nz/~smr26/wordpress/2009/01/19/MONOTIC-time-in-mac-os-x/

谢谢!
Brett

Mach内核提供对系统时钟的访问,其中至少有一个(
系统时钟
)是单调递增的

#include <mach/clock.h>
#include <mach/mach.h>

clock_serv_t cclock;
mach_timespec_t mts;

host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
#包括
#包括
时钟服务锁;
马赫时标;
主机获取时钟服务(mach\u host\u self()、系统时钟和时钟锁);
时钟获取时间(cclock和mts);
马赫数端口释放(马赫数任务自身(),cclock);
mach\u timespec\t
具有纳秒级精度。不过,我不确定精度

Mac OS X支持三个时钟:

  • 系统时钟
    返回自启动时间起的时间
  • 日历时钟
    返回自1970-01-01以来的UTC时间
  • REALTIME_-CLOCK
    已被弃用,在其当前实现中与
    SYSTEM_-CLOCK
    相同

据说时钟是单调递增的,除非有人调用
clock\u set\u time
。调用
clock\u set\u time
是因为它可以打破时钟的单调特性,事实上,不做任何事情。

在查找了一些不同的答案后,我最终定义了一个模拟clock\u ge的头t马赫数上的时间:

#include <sys/types.h>
#include <sys/_types/_timespec.h>
#include <mach/mach.h>
#include <mach/clock.h>

#ifndef mach_time_h
#define mach_time_h

/* The opengroup spec isn't clear on the mapping from REALTIME to CALENDAR
 being appropriate or not.
 http://pubs.opengroup.org/onlinepubs/009695299/basedefs/time.h.html */

// XXX only supports a single timer
#define TIMER_ABSTIME -1
#define CLOCK_REALTIME CALENDAR_CLOCK
#define CLOCK_MONOTONIC SYSTEM_CLOCK

typedef int clockid_t;

/* the mach kernel uses struct mach_timespec, so struct timespec
    is loaded from <sys/_types/_timespec.h> for compatability */
// struct timespec { time_t tv_sec; long tv_nsec; };

int clock_gettime(clockid_t clk_id, struct timespec *tp);

#endif
#包括
#包括
#包括
#包括
#ifndef马赫时间
#定义马赫数时间
/*opengroup规范不清楚从实时到日历的映射
合适与否。
http://pubs.opengroup.org/onlinepubs/009695299/basedefs/time.h.html */
//XXX只支持一个计时器
#定义计时器\u ABSTIME-1
#定义时钟\实时日历\时钟
#定义时钟\u单调系统\u时钟
typedef int clockid\t;
/*mach内核使用struct mach_timespec,因此struct timespec
从中加载以实现兼容性*/
//结构timespec{time\t tv\u sec;long tv\u nsec;};
int clock_gettime(clockid_t clk_id,struct timespec*tp);
#恩迪夫
用mach_gettime.c

#include "mach_gettime.h"
#include <mach/mach_time.h>

#define MT_NANO (+1.0E-9)
#define MT_GIGA UINT64_C(1000000000)

// TODO create a list of timers,
static double mt_timebase = 0.0;
static uint64_t mt_timestart = 0;

// TODO be more careful in a multithreaded environement
int clock_gettime(clockid_t clk_id, struct timespec *tp)
{
    kern_return_t retval = KERN_SUCCESS;
    if( clk_id == TIMER_ABSTIME)
    {
        if (!mt_timestart) { // only one timer, initilized on the first call to the TIMER
            mach_timebase_info_data_t tb = { 0 };
            mach_timebase_info(&tb);
            mt_timebase = tb.numer;
            mt_timebase /= tb.denom;
            mt_timestart = mach_absolute_time();
        }

        double diff = (mach_absolute_time() - mt_timestart) * mt_timebase;
        tp->tv_sec = diff * MT_NANO;
        tp->tv_nsec = diff - (tp->tv_sec * MT_GIGA);
    }
    else // other clk_ids are mapped to the coresponding mach clock_service
    {
        clock_serv_t cclock;
        mach_timespec_t mts;

        host_get_clock_service(mach_host_self(), clk_id, &cclock);
        retval = clock_get_time(cclock, &mts);
        mach_port_deallocate(mach_task_self(), cclock);

        tp->tv_sec = mts.tv_sec;
        tp->tv_nsec = mts.tv_nsec;
    }

    return retval;
}
#包括“mach_gettime.h”
#包括
#定义MT_NANO(+1.0E-9)
#定义MT_GIGA UINT64_C(100000000)
//TODO创建计时器列表,
静态双mt_时基=0.0;
静态uint64\u t mt\u timestart=0;
//在多线程环境中要更加小心
int clock\u gettime(clockid\u t clk\u id,struct timespec*tp)
{
kern_return_t retval=kern_SUCCESS;
如果(时钟id==定时器时间)
{
如果(!mt_timestart){//只有一个计时器,则在第一次调用计时器时初始化
马赫时基信息数据tb={0};
马赫时基信息(&tb);
mt_时基=tb.numer;
mt_时基/=tb.denom;
mt_timestart=mach_绝对时间();
}
双差=(马赫绝对时间()-mt时间开始)*mt时间基准;
tp->tv_sec=diff*MT_NANO;
tp->tv\u nsec=diff-(tp->tv\u sec*MT\u GIGA);
}
else//其他clk_ID映射到相应的马赫时钟服务
{
时钟服务锁;
马赫时标;
主机获取时钟服务(mach\u host\u self()、时钟id和时钟锁);
retval=时钟获取时间(cclock和mts);
马赫数端口释放(马赫数任务自身(),cclock);
tp->tv_sec=mts.tv_sec;
tp->tv\u nsec=mts.tv\u nsec;
}
返回返回;
}

只需使用马赫时间
它是一个公共API,可以在macOS、iOS和tvOS上运行,并且可以在沙箱中运行

Mach Time返回一个抽象的时间单位,我通常称之为“时钟滴答声”。时钟滴答声的长度是系统特定的,取决于CPU。在当前的Intel系统上,时钟滴答声实际上正好是一纳秒,但你不能依赖它(ARM可能不同,PowerPC CPU肯定不同)。系统还可以告诉您将时钟信号转换为纳秒以及将纳秒转换为时钟信号的转换系数(该系数是静态的,在运行时不会改变)。当系统启动时,时钟从
0
开始,然后随着其后的每个时钟滴答声单调增加,因此您也可以使用马赫时间来获得系统的正常运行时间(当然,正常运行时间是单调的!)

下面是一些代码:

#include <stdio.h>
#include <inttypes.h>
#include <mach/mach_time.h>

int main ( ) {
    uint64_t clockTicksSinceSystemBoot = mach_absolute_time();
    printf("Clock ticks since system boot: %"PRIu64"\n",
        clockTicksSinceSystemBoot
    );

    static mach_timebase_info_data_t timebase;
    mach_timebase_info(&timebase);
    // Cast to double is required to make this a floating point devision,
    // otherwise it would be an interger division and only the result would
    // be converted to floating point!
    double clockTicksToNanosecons = (double)timebase.numer / timebase.denom;

    uint64_t systemUptimeNanoseconds = (uint64_t)(
        clockTicksToNanosecons * clockTicksSinceSystemBoot
    );
    uint64_t systemUptimeSeconds = systemUptimeNanoseconds / (1000 * 1000 * 1000);
    printf("System uptime: %"PRIu64" seconds\n", systemUptimeSeconds);
}
由于马赫时间与任何挂钟时间都没有关系,您可以随意使用系统日期和时间设置,这不会对马赫时间产生任何影响

不过,有一个特殊的考虑因素可能会使马赫时间不适合某些用例:T当系统处于休眠状态时,CPU时钟没有运行!因此,如果让线程等待5分钟,1分钟后系统进入休眠状态并保持休眠状态30分钟,则线程仍在等待4分钟r由于30分钟的睡眠时间不算在内,系统已经醒来!CPU时钟在这段时间内也在休息。但在其他情况下,这是错误的
// Sleep for 750 ns
uint64_t machTimeNow = mach_absolute_time();
uint64_t clockTicksToSleep = (uint64_t)(750 / clockTicksToNanosecons);
uint64_t machTimeIn750ns = machTimeNow + clockTicksToSleep;
mach_wait_until(machTimeIn750ns);
// Measure time
uint64_t machTimeBegin = mach_absolute_time();
sleep(1);
uint64_t machTimeEnd = mach_absolute_time();
uint64_t machTimePassed = machTimeEnd - machTimeBegin;
uint64_t timePassedNS = (uint64_t)(
    machTimePassed * clockTicksToNanosecons
);
printf("Thread slept for: %"PRIu64" ns\n", timePassedNS);