13

I'm looking for high-resolution timing code for iPhone, in order to do some performance timings. I'd like to write code like this:

HighResolutionTimer* myTimer = [[HighResolutionTimer alloc]init];
[myTimer start];
[self doSomeLengthyOperation];
NSLog( @"doSomeLengthyOperation took %f seconds", [myTimer elapsedTime] );
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
zpasternack
  • 17,838
  • 2
  • 63
  • 81

4 Answers4

26

Look into mach_absolute_time() in the mach/mach_time.h header.

Don't use NSDate. NSDate isn't even guaranteed to not go backwards occasionally, when ntp does its thing.

(Devices can have clock drift. If the iOS device drifts fast a few seconds, then when NTP corrects this drift, you will see the clock suddenly go backwards a few seconds. Very bad for timing use. mach_time uses a counter that doesn't ever get corrected by NTP, thus can't go backwards, thus is far better for timing.)

hotpaw2
  • 70,107
  • 14
  • 90
  • 153
  • 1
    `NSDate` just returns a double that counts the seconds since Jan 1 2001. For stopwatch timing, a negative time just won't occur, so I see no need for the overhead of bringing `mach_absolute_time()` into your code -- [both implemented here](http://stackoverflow.com/a/12553393/111307). – bobobobo Sep 23 '12 at 15:52
  • 3
    @bobobobo : The device's count of seconds since Jan 1 2001 occasionally gets corrected backwards by NTP. So it can be non-monotonic on any given device that has a clock crystal that sometimes drifts fast, plus a network connection that can correct this drift. – hotpaw2 Sep 23 '12 at 18:03
  • I ended up using mach_absolute_time(). I found a little class using it, [here](http://zpasternack.blogspot.com/2012/07/high-resolution-timing-in-cocoa.html). – zpasternack Sep 23 '12 at 19:08
  • @howpaw2 That is a very fine point, you should work that explanation of drifting clocks into your answer. Practically, I could see this showing up as a very strange bug a developer would have a really, really hard time finding, or even repeating. – bobobobo Jan 10 '13 at 16:13
16

A better option is CACurrentMediaTime() which uses mach_absolute_time() but converts it to a CFTimeInterval (i.e., time in seconds as a double) for you.

Pang
  • 9,564
  • 146
  • 81
  • 122
Peter N Lewis
  • 17,664
  • 2
  • 43
  • 56
8

Here's my answer for clock timers using both mach_absolute_time(), based on compute method shown here, and NSDate. The are actually the same in terms of accuracy.

Mach version

double machGetClockS()
{
  static bool init = 0 ;
  static mach_timebase_info_data_t tbInfo ;
  static double conversionFactor ;
  if(!init)
  {
    init = 1 ;
    // get the time base
    mach_timebase_info( &tbInfo ) ;
    conversionFactor = tbInfo.numer / (1e9*tbInfo.denom) ; // ns->s
  }

  return mach_absolute_time() * conversionFactor ; // seconds
}

double machGetClockDiffS()
{
  static double lastTime = 0;

  double currentTime = machGetClockS() ;

  double diff = currentTime - lastTime ;

  lastTime = currentTime ; // update for next call

  return diff ; // that's your answer
}

NSTimeInterval version

double getClockS()
{
  return [NSDate timeIntervalSinceReferenceDate] ; // NSTimeInterval is always specified in seconds 
}

double getClockDiffS()
{
  static double lastTime = 0 ;

  double currentTime = getClockS() ;

  double diff = currentTime - lastTime ;

  lastTime = currentTime ; // update for next call

  return diff ; // that's your answer
}

Results:

Note the resolution on both of these is really good.


IOS SIMULATOR, running frame rate counts (in milliseconds (*1000.0))

MACH_ABS_TIME / NSTimeIntervals
58.557001 / 58.552980
40.558007 / 40.562987
52.207822 / 52.200019
33.742197 / 33.742011
38.498912 / 38.504004
48.872679 / 48.868001
45.012602 / 45.011997
57.858432 / 57.865977
25.044615 / 25.038004


IPAD HARDWARE SAMPLINGS:
33.415041 / 33.416033
33.240167 / 33.239007
33.357542 / 33.357978
33.302833 / 33.302009
33.506750 / 33.509016
33.582250 / 33.582985
33.233958 / 33.232987
33.239042 / 33.237994

*If you look at the edit history of this post, you can see the danger of using float in the place of a double!

bobobobo
  • 64,917
  • 62
  • 258
  • 363
  • You forgot to add init = true; in machGetClockS(). – Andrew Smith Oct 09 '13 at 23:12
  • Not a good test of time resolution, since you tested without wireshark monitoring for any NTP network connections, which can sometimes really mess with the NSDate results. – hotpaw2 May 04 '15 at 18:25
6

Use NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate] to get a start time, and then NSLog (@"Operation took %f seconds.", [NSDate timeIntervalSinceReferenceDate] - startTime); at the end.

lucius
  • 8,665
  • 3
  • 34
  • 41