How can I use mach_absolute_time without overflowing?

后端 未结 2 1688
甜味超标
甜味超标 2020-12-14 20:49

On Darwin, the POSIX standard clock_gettime(CLOCK_MONOTONIC) timer is not available. Instead, the highest resolution monotonic timer is obtained through the

2条回答
  •  [愿得一人]
    2020-12-14 21:13

    You're worrying about overflow when multiplying/dividing with values from the mach_timebase_info struct, which is used for conversion to nanoseconds. So, while it may not fit your exact needs, there are easier ways to get a count in nanoseconds or seconds.

    All solutions below are using mach_absolute_time internally (and NOT the wall clock).


    Use double instead of uint64_t

    (supported in Objective-C and Swift)

    double tbInSeconds = 0;
    mach_timebase_info_data_t tb;
    kern_return_t kError = mach_timebase_info(&tb);
    if (kError == 0) {
        tbInSeconds = 1e-9 * (double)tb.numer / (double)tb.denom;
    }
    

    (remove the 1e-9 if you want nanoseconds)

    Usage:

    uint64_t start = mach_absolute_time();
    // do something
    uint64_t stop = mach_absolute_time();
    double durationInSeconds = tbInSeconds * (stop - start);
    

    Use ProcessInfo.processInfo.systemUptime

    (supported in Objective-C and Swift)

    It does the job in double seconds directly:

    CFTimeInterval start = NSProcessInfo.processInfo.systemUptime;
    // do something
    CFTimeInterval stop = NSProcessInfo.processInfo.systemUptime;
    NSTimeInterval durationInSeconds = stop - start;
    

    For reference, source code of systemUptime just does something similar as previous solution:

    struct mach_timebase_info info;
    mach_timebase_info(&info);
    __CFTSRRate = (1.0E9 / (double)info.numer) * (double)info.denom;
    __CF1_TSRRate = 1.0 / __CFTSRRate;
    uint64_t tsr = mach_absolute_time();
    return (CFTimeInterval)((double)tsr * __CF1_TSRRate);
    

    Use QuartzCore.CACurrentMediaTime()

    (supported in Objective-C and Swift)

    Same as systemUptime, but without being open source.


    Use Dispatch.DispatchTime.now()

    (supported in Swift only)

    Another wrapper around mach_absolute_time(). Base precision is nanoseconds, backed with UInt64.

    DispatchTime start = DispatchTime.now()
    // do something
    DispatchTime stop = DispatchTime.now()
    TimeInterval durationInSeconds = Double(end.uptimeNanoseconds - start.uptimeNanoseconds) / 1_000_000_000
    

    For reference, source code of DispatchTime.now() says it basically simply returns a struct DispatchTime(rawValue: mach_absolute_time()). And the calculation for uptimeNanoseconds is:

    (result, overflow) = result.multipliedReportingOverflow(by: UInt64(DispatchTime.timebaseInfo.numer))
    result = overflow ? UInt64.max : result / UInt64(DispatchTime.timebaseInfo.denom)
    

    So it just discards results if the multiplication can't be stored in an UInt64.

提交回复
热议问题