Reputation: 40329
Is there an easy way to get a time very precisely?
I need to calculate some delays between method calls. More specifically, I want to calculate the speed of scrolling in an UIScrollView.
Upvotes: 106
Views: 119001
Reputation: 82535
mach_absolute_time()
can be used to get precise measurements.
See http://developer.apple.com/qa/qa2004/qa1398.html
Also available is CACurrentMediaTime()
, which is essentially the same thing but with an easier-to-use interface.
(Note: This answer was written in 2009. See Pavel Alexeev's answer for the simpler POSIX clock_gettime()
interfaces available in newer versions of macOS and iOS.)
Upvotes: 42
Reputation: 6081
Functions based on mach_absolute_time
are good for short measurements.
But for long measurements important caveat is that they stop ticking while device is asleep.
There is a function to get time since boot. It doesn't stop while sleeping. Also, gettimeofday
is not monotonic, but in my experiments I've always see that boot time changes when system time changes, so I think it should work fine.
func timeSinceBoot() -> TimeInterval
{
var bootTime = timeval()
var currentTime = timeval()
var timeZone = timezone()
let mib = UnsafeMutablePointer<Int32>.allocate(capacity: 2)
mib[0] = CTL_KERN
mib[1] = KERN_BOOTTIME
var size = MemoryLayout.size(ofValue: bootTime)
var timeSinceBoot = 0.0
gettimeofday(¤tTime, &timeZone)
if sysctl(mib, 2, &bootTime, &size, nil, 0) != -1 && bootTime.tv_sec != 0 {
timeSinceBoot = Double(currentTime.tv_sec - bootTime.tv_sec)
timeSinceBoot += Double(currentTime.tv_usec - bootTime.tv_usec) / 1000000.0
}
return timeSinceBoot
}
And since iOS 10 and macOS 10.12 we can use CLOCK_MONOTONIC:
if #available(OSX 10.12, *) {
var uptime = timespec()
if clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) == 0 {
return Double(uptime.tv_sec) + Double(uptime.tv_nsec) / 1000000000.0
}
}
To sum it up:
Date.timeIntervalSinceReferenceDate
— changes when system time changes, not monotonicCFAbsoluteTimeGetCurrent()
— not monotonic, may go backwardCACurrentMediaTime()
— stops ticking when device is asleeptimeSinceBoot()
— doesn't sleep, but might be not monotonicCLOCK_MONOTONIC
— doesn't sleep, monotonic, supported since iOS 10Upvotes: 7
Reputation: 400314
CFAbsoluteTimeGetCurrent()
returns the absolute time as a double
value, but I don't know what its precision is -- it might only update every dozen milliseconds, or it might update every microsecond, I don't know.
Upvotes: 16
Reputation: 3692
You can get current time in milliseconds since January 1st, 1970 using an NSDate:
- (double)currentTimeInMilliseconds {
NSDate *date = [NSDate date];
return [date timeIntervalSince1970]*1000;
}
Upvotes: 0
Reputation: 23449
For those we need the Swift version of the answer of @Jeff Thompson:
// Get a current time for where you want to start measuring from
var date = NSDate()
// do work...
// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
var timePassed_ms: Double = date.timeIntervalSinceNow * -1000.0
I hope this help you.
Upvotes: -2
Reputation: 7122
Please do not use NSDate
, CFAbsoluteTimeGetCurrent
, or gettimeofday
to measure elapsed time. These all depend on the system clock, which can change at any time due to many different reasons, such as network time sync (NTP) updating the clock (happens often to adjust for drift), DST adjustments, leap seconds, and so on.
This means that if you're measuring your download or upload speed, you will get sudden spikes or drops in your numbers that don't correlate with what actually happened; your performance tests will have weird incorrect outliers; and your manual timers will trigger after incorrect durations. Time might even go backwards, and you end up with negative deltas, and you can end up with infinite recursion or dead code (yeah, I've done both of these).
Use mach_absolute_time
. It measures real seconds since the kernel was booted. It is monotonically increasing (will never go backwards), and is unaffected by date and time settings. Since it's a pain to work with, here's a simple wrapper that gives you NSTimeInterval
s:
// LBClock.h
@interface LBClock : NSObject
+ (instancetype)sharedClock;
// since device boot or something. Monotonically increasing, unaffected by date and time settings
- (NSTimeInterval)absoluteTime;
- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute;
@end
// LBClock.m
#include <mach/mach.h>
#include <mach/mach_time.h>
@implementation LBClock
{
mach_timebase_info_data_t _clock_timebase;
}
+ (instancetype)sharedClock
{
static LBClock *g;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
g = [LBClock new];
});
return g;
}
- (id)init
{
if(!(self = [super init]))
return nil;
mach_timebase_info(&_clock_timebase);
return self;
}
- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute
{
uint64_t nanos = (machAbsolute * _clock_timebase.numer) / _clock_timebase.denom;
return nanos/1.0e9;
}
- (NSTimeInterval)absoluteTime
{
uint64_t machtime = mach_absolute_time();
return [self machAbsoluteToTimeInterval:machtime];
}
@end
Upvotes: 30
Reputation: 6012
#define CTTimeStart() NSDate * __date = [NSDate date]
#define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1)
Usage:
CTTimeStart();
...
CTTimeEnd(@"that was a long time:");
Output:
2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023
Upvotes: 5
Reputation: 2293
I would NOT use mach_absolute_time()
because it queries a combination of the kernel and the processor for an absolute time using ticks (probably an uptime).
What I would use:
CFAbsoluteTimeGetCurrent();
This function is optimized to correct the difference in the iOS and OSX software and hardware.
Something Geekier
The quotient of a difference in mach_absolute_time()
and AFAbsoluteTimeGetCurrent()
is always around 24000011.154871
Here is a log of my app:
Please note that final result time is a difference in CFAbsoluteTimeGetCurrent()
's
2012-03-19 21:46:35.609 Rest Counter[3776:707] First Time: 353900795.609040
2012-03-19 21:46:36.360 Rest Counter[3776:707] Second Time: 353900796.360177
2012-03-19 21:46:36.361 Rest Counter[3776:707] Final Result Time (difference): 0.751137
2012-03-19 21:46:36.363 Rest Counter[3776:707] Mach absolute time: 18027372
2012-03-19 21:46:36.365 Rest Counter[3776:707] Mach absolute time/final time: 24000113.153295
2012-03-19 21:46:36.367 Rest Counter[3776:707] -----------------------------------------------------
2012-03-19 21:46:43.074 Rest Counter[3776:707] First Time: 353900803.074637
2012-03-19 21:46:43.170 Rest Counter[3776:707] Second Time: 353900803.170256
2012-03-19 21:46:43.172 Rest Counter[3776:707] Final Result Time (difference): 0.095619
2012-03-19 21:46:43.173 Rest Counter[3776:707] Mach absolute time: 2294833
2012-03-19 21:46:43.175 Rest Counter[3776:707] Mach absolute time/final time: 23999753.727777
2012-03-19 21:46:43.177 Rest Counter[3776:707] -----------------------------------------------------
2012-03-19 21:46:46.499 Rest Counter[3776:707] First Time: 353900806.499199
2012-03-19 21:46:55.017 Rest Counter[3776:707] Second Time: 353900815.016985
2012-03-19 21:46:55.018 Rest Counter[3776:707] Final Result Time (difference): 8.517786
2012-03-19 21:46:55.020 Rest Counter[3776:707] Mach absolute time: 204426836
2012-03-19 21:46:55.022 Rest Counter[3776:707] Mach absolute time/final time: 23999996.639500
2012-03-19 21:46:55.024 Rest Counter[3776:707] -----------------------------------------------------
Upvotes: 10
Reputation: 2651
I know this is an old one but even I found myself wandering past it again, so I thought I'd submit my own option here.
Best bet is to check out my blog post on this: Timing things in Objective-C: A stopwatch
Basically, I wrote a class that does stop watching in a very basic way but is encapsulated so that you only need to do the following:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
And you end up with:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
in the log...
Again, check out my post for a little more or download it here: MMStopwatch.zip
Upvotes: 1
Reputation: 2711
NSDate
and the timeIntervalSince*
methods will return a NSTimeInterval
which is a double with sub-millisecond accuracy. NSTimeInterval
is in seconds, but it uses the double to give you greater precision.
In order to calculate millisecond time accuracy, you can do:
// Get a current time for where you want to start measuring from
NSDate *date = [NSDate date];
// do work...
// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
double timePassed_ms = [date timeIntervalSinceNow] * -1000.0;
Documentation on timeIntervalSinceNow.
There are many other ways to calculate this interval using NSDate
, and I would recommend looking at the class documentation for NSDate
which is found in NSDate Class Reference.
Upvotes: 127
Reputation: 2478
Also, here is how to calculate a 64-bit NSNumber
initialized with the Unix epoch in milliseconds, in case that is how you want to store it in CoreData. I needed this for my app which interacts with a system that stores dates this way.
+ (NSNumber*) longUnixEpoch {
return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
}
Upvotes: 5