iPhone的高分辨率计时器?

13

我正在寻找用于iPhone的高分辨率定时代码,以便进行一些性能定时。我想编写如下代码:

HighResolutionTimer* myTimer = [[HighResolutionTimer alloc]init];
[myTimer start];
[self doSomeLengthyOperation];
NSLog( @"doSomeLengthyOperation took %f seconds", [myTimer elapsedTime] );

1
为什么不直接使用 getrusage()?在 C 中可用的任何东西都可以在 Objective-C 中使用。 - Carl Norum
1
因为有一个完美的Cocoa方法可以做同样的事情。 - lucius
4个回答

26

请查阅mach/mach_time.h头文件中的mach_absolute_time()函数。

不要使用NSDate。当ntp工作时,NSDate甚至不能保证不偶尔出现向后移动的情况。

(设备可能存在时钟漂移。如果iOS设备快速漂移几秒钟,那么当NTP纠正此漂移时,您会看到时钟突然向后退几秒钟。对于计时应用非常不利。mach_time使用一个计数器,该计数器不会受到NTP校正,因此无法向后移动,因此更适合计时。)


1
NSDate 只返回一个双精度浮点数,它计算自2001年1月1日以来的秒数。对于秒表计时,不会出现负时间,因此我认为没有必要将 mach_absolute_time() 引入您的代码中 -- 两者都在这里实现 - bobobobo
3
该设备自2001年1月1日以来经过的秒数偶尔会被NTP回溯校正。因此,在具有时钟晶体并且有时会快速漂移的任何给定设备上,加上可以纠正此漂移的网络连接,它可能是非单调的。 - hotpaw2
我最终使用了mach_absolute_time()。我找到了一个使用它的小类,在这里 - zpasternack
@howpaw2,这是一个非常好的观点,你应该把时钟漂移的解释融入到你的答案中。实际上,我可以看到这可能会出现为一个非常奇怪的 bug,开发人员将会非常难以找到或重复。 - bobobobo

16

一种更好的选择是使用CACurrentMediaTime(),它使用mach_absolute_time(),但会自动将其转换为CFTimeInterval(即以浮点数形式表示的秒数)。



8

以下是使用mach_absolute_time()NSDate的时钟计时器的答案,基于此处所示的计算方法。它们在精度方面实际上是相同的。

Mach版本

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
}

结果:

注意这两个的分辨率非常好。

iOS模拟器,运行帧速计数(以毫秒为单位(*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硬件采样: 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

*如果您查看此帖子的编辑历史记录,您可以看到在使用float替换double的危险!


你忘记在machGetClockS()中添加init = true;了。 - Andrew Smith
这不是一个很好的时间分辨率测试,因为您在没有使用Wireshark监视任何NTP网络连接的情况下进行了测试,这可能会对NSDate结果产生实质性影响。 - hotpaw2

6

使用NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate]获取开始时间,然后在结尾处使用NSLog(@"操作花费%f秒。", [NSDate timeIntervalSinceReferenceDate] - startTime);


1
非常好,谢谢!我没有意识到NSDate有这种精度水平。 - zpasternack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接