Mac OS X 中的clock_gettime 替代方案
Posted
技术标签:
【中文标题】Mac OS X 中的clock_gettime 替代方案【英文标题】:clock_gettime alternative in Mac OS X 【发布时间】:2011-07-07 05:44:40 【问题描述】:在通过 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.)
clock_gettime
似乎没有在 Mac OS X 中实现。是否有其他方法可以在 纳秒 中获取 纪元时间?不幸的是,gettimeofday
在 微秒 内。
【问题讨论】:
我的文档说 “所有实现都支持系统范围的实时时钟,由 CLOCK_REALTIME 标识。” 你#include <time.h>
了吗?
这没有帮助。 clock_gettime
未在 Mac OS X 中实现。
我知道。它在我的链接器命令行中。我根本没有达到链接阶段。 Mac OS X 没有clock_gettime
,而 Linux 有。
我为此写了一个快速包装器:gist.github.com/alfwatt/3588c5aa1f7a1ef7a3bb
请注意,macOS Sierra 10.12(2016 年 9 月,XCode 8)及更高版本直接支持 clock_gettime()
— 如 James Wald 在此 answer 中所述。
【参考方案1】:
实际上,它似乎没有在 Sierra 10.12 之前的 macOS 上实现。你可能想看看这个blog entry。主要思想在以下代码sn-p中:
#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;
【讨论】:
我不想要单调的时间,我想要自纪元以来的实时时间,以纳秒为单位。 @Delan,我不明白你为什么想要那个,对于以年为单位的东西来说,这是无用的精确度。通常你需要纳秒来为一个函数计时。然后就像在那个博客中所做的那样,在之前和之后花时间就足够了。但是你总是可以通过在程序开头使用gettimeofday
和mach_absolute_time
来模拟它,然后将它们相加。
永远不要混淆单调和实时。当 NTP 守护程序更正系统时钟时,实时时间可能会跳跃。它们是两个完全不同的东西,真的。
“博客条目”现已损坏 :(
请改用此链接:web.archive.org/web/20100517095152/http://www.wand.net.nz/…【参考方案2】:
Technical Q&A QA1398: Technical Q&A QA1398: Mach Absolute Time Units中描述了你需要的一切,基本上你想要的功能是mach_absolute_time
。
这是该页面中示例代码的稍早版本,它使用 Mach 调用完成所有操作(当前版本使用来自 CoreServices 的 AbsoluteToNanoseconds
)。在当前的 OS X(即在 x86_64 上的 Snow Leopard 上)中,绝对时间值实际上以纳秒为单位,因此实际上根本不需要任何转换。所以,如果你擅长编写可移植的代码,你就会转换,但如果你只是为自己做一些快速而肮脏的事情,你就不必费心了。
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;
【讨论】:
【参考方案3】:经过数小时仔细阅读不同的答案、博客和标题后,我找到了一种获取当前时间的便携方法:
#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
希望这可以节省一些时间。干杯!
【讨论】:
host_get_clock_service 贵吗?为该过程缓存它是否值得?它可以重复使用吗?线程安全?谢谢 - :) 还需要包括:#ifdef __MACH__ #include__MACH__
代码进行了计时吗?使用独立的微秒计时器(经过充分测试)我得到的印象是,上述代码的两次调用花费了大约 25 微秒。
有谁知道@peterk 问题的答案。在我的测试中,似乎缓存它会使性能翻倍,但我是否需要为每个线程缓存它,或者我可以创建一个并在所有线程之间共享它?【参考方案4】:
我尝试了使用clock_get_time 的版本,并缓存了host_get_clock_service 调用。它比 gettimeofday 慢得多,每次调用需要几微秒。而且,更糟糕的是,返回值的步长为 1000,即仍然是微秒粒度。
我建议使用 gettimeofday,并将 tv_usec 乘以 1000。
【讨论】:
gettimeofday
返回的时间可能是非单调的。它可能会剧烈跳跃(例如,当夏令时发生时),或者它甚至可能以稍微更慢/更快(!)的速度运行,在时间服务器同步发生之后,时间服务可能希望避免对时间进行小的突然变化。
【参考方案5】:
#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
【讨论】:
如果您使用 CLOCK_REALTIME 或 CLOCK_MONOTONIC,您还应该定义这些:#define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 0 因为 a) 精度降低了 1000 倍(尽管这还不错,因为clock_gettime
真正返回的时间粒度很少(永远)是一纳秒),并且 b) 时间gettimeofday
返回的不同且非单调的。它可能会剧烈跳跃(例如,当夏令时发生时),或者它甚至可能以稍微更慢/更快(!)的速度运行,在时间服务器同步发生之后,时间服务可能希望避免对时间进行小的突然变化。
不幸的是,一天中的时间不是一个单调的时钟。 (单调时钟专门用于避免与时间时钟相关的所有问题,包括管理员或 NTP 更改时间、闰秒、时区等)【参考方案6】:
迄今为止,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
(2GHz CPU)。如果您需要单次执行的“时间演变”,请使用此选项。否则循环你的代码10000
次和配置文件,即使使用gettimeofday()
,它是可移植的(POSIX),并且具有100ns +/- 0.5ns
的延迟(尽管只有1us
粒度)。
【讨论】:
知道转换为 double 对精度的影响吗? AFAIK 双精度只有 53 位,mach_absolute_time
可以返回 64 位范围内的任何内容。只是好奇。【参考方案7】:
感谢您的帖子
我认为你可以添加以下几行
#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
让我知道您对延迟和粒度的了解
【讨论】:
如果你缓存mach_timebase_info
调用会更好(也许用一个静态变量来保持它的整洁)。 mach_timebase_info()
是一个系统调用,在我的机器上需要大约 180ns。与 mach_absolute_time()
的 ~22ns 不同,它基本上只是采样 rdtsc
。【参考方案8】:
以上解决方案都没有回答这个问题。要么他们没有给你绝对的 Unix 时间,要么他们的准确度是 1 微秒。 jbenet 最流行的解决方案很慢(~6000ns),即使它的返回表明如此,也不以纳秒为单位。以下是 jbenet 和 Dmitri B 建议的 2 个解决方案的测试,以及我对此的看法。您无需更改即可运行代码。
第三种解决方案确实以纳秒为单位计算,并为您提供相当快的绝对 Unix 时间(~90ns)。因此,如果有人觉得它有用 - 请在这里告诉我们所有人:-)。我会坚持 Dmitri B 的那个(代码中的解决方案 #1)——它更适合我的需要。
我需要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(µ, 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(µ, 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;
【讨论】:
仅供参考。在我的系统上,调用mach_absolute_time()
加上乘以info.numer / info.denom
每次调用大约需要33ns:基准:gist.github.com/aktau/9f52f812200d8d69a5d1 libuv 问题:github.com/joyent/libuv/pull/1325【参考方案9】:
至少早在 Mountain Lion 之前,mach_absolute_time()
返回 纳秒 而不是绝对时间(即总线周期数)。
以下代码在我的 MacBook Pro (2 GHz Core i7) 上显示调用 mach_absolute_time()
的时间在 10 次运行中平均为 39 ns(最小 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;
【讨论】:
IIRC,所有 Intel 硬件都使用纳秒,所有 PowerPC 硬件都使用总线周期,无论操作系统版本如何,但 G5 可能使用纳秒——我忘了。【参考方案10】:基于开源mach_absolute_time.c,我们可以看到extern mach_port_t clock_port;
行告诉我们已经为单调时间初始化了一个马赫端口。可以直接访问此时钟端口,而无需调用 mach_absolute_time
然后将 back 转换为 struct timespec
。绕过对mach_absolute_time
的调用应该可以提高性能。
我使用基于外部clock_port
和similar thread 的代码创建了一个小的Github repo (PosixMachTiming)。 PosixMachTiming 模拟 clock_gettime
的 CLOCK_REALTIME
和 CLOCK_MONOTONIC
。它还模拟绝对单调时间的函数clock_nanosleep
。请试一试,看看性能比较。也许您可能想要创建比较测试或模拟其他 POSIX 时钟/功能?
【讨论】:
【参考方案11】:请注意,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);
它确实提供纳秒;但是,分辨率为 1000,因此它(不)有效地限制为微秒:
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 或更早版本)上运行。
【讨论】:
我使用的是 macOS 10.12 但无法编译上述内容;我得到Use of undeclared identifier 'CLOCK_REALTIME'
奇怪,应该可以使用:#include xcode-slect --install
使用的数据感到偏执!
@JamesWald:我在指向您的答案的问题下发表了评论。对评论进行投票将提高其可见度(几个点赞会更好)。
请注意:如果您使用 XCode 8(或更高版本)构建但目标 OSX 版本早于 10.12,您将在编译时找到 clock_gettime
符号,但在 10.11 上运行二进制文件时或者在下面你会得到“dyld: Symbol not found: _clock_gettime”,因为编译器生成了一个弱符号。【参考方案12】:
我找到了另一个便携式解决方案。
在一些头文件中声明(甚至在你的源文件中):
/* 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>
。
【讨论】:
【参考方案13】: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
【讨论】:
以上是关于Mac OS X 中的clock_gettime 替代方案的主要内容,如果未能解决你的问题,请参考以下文章