如何以毫秒为单位记录方法的执行时间?

时间:2010-01-25 01:57:47

标签: ios objective-c optimization time

有没有办法确定方法需要执行多长时间(以毫秒为单位)?

20 个答案:

答案 0 :(得分:422)

NSDate *methodStart = [NSDate date];

/* ... Do whatever you need to do ... */

NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

<强>夫特:

let methodStart = NSDate()

/* ... Do whatever you need to do ... */

let methodFinish = NSDate()
let executionTime = methodFinish.timeIntervalSinceDate(methodStart)
print("Execution time: \(executionTime)")

<强> Swift3:

let methodStart = Date()

/* ... Do whatever you need to do ... */

let methodFinish = Date()
let executionTime = methodFinish.timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")

易于使用且具有亚毫秒精度。

答案 1 :(得分:247)

以下是我使用的两个单行宏:

#define TICK   NSDate *startTime = [NSDate date]
#define TOCK   NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])

像这样使用:

TICK;

/* ... Do Some Work Here ... */

TOCK;

答案 2 :(得分:48)

对于OS X上的细粒度计时,您应该使用mach_absolute_time( )中声明的<mach/mach_time.h>

#include <mach/mach_time.h>
#include <stdint.h>

// Do some stuff to setup for timing
const uint64_t startTime = mach_absolute_time();
// Do some stuff that you want to time
const uint64_t endTime = mach_absolute_time();

// Time elapsed in Mach time units.
const uint64_t elapsedMTU = endTime - startTime;

// Get information for converting from MTU to nanoseconds
mach_timebase_info_data_t info;
if (mach_timebase_info(&info))
   handleErrorConditionIfYoureBeingCareful();

// Get elapsed time in nanoseconds:
const double elapsedNS = (double)elapsedMTU * (double)info.numer / (double)info.denom;

当然,关于细粒度测量的常见警告适用;你可能最好多次调用被测例程,平均/采取最小/其他形式的处理。

此外,请注意您可能会发现使用Shark等工具配置文件运行应用程序会更有用。这不会为您提供准确的时间信息,但它会告诉您应用程序的时间百分比在哪里,这通常更有用(但并非总是如此)。

答案 3 :(得分:12)

在Swift中,我使用:

在我的Macros.swift中,我刚刚添加了

var startTime = NSDate()
func TICK(){ startTime =  NSDate() }
func TOCK(function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__){
    println("\(function) Time: \(startTime.timeIntervalSinceNow)\nLine:\(line) File: \(file)")
}

你现在可以随时随地打电话

TICK()

// your code to be tracked

TOCK()
  • 此代码基于Ron的代码转换为Swift,他有学分
  • 我在全球范围内使用开始日期,欢迎任何改进建议

答案 4 :(得分:9)

我知道这是一个旧的,但即使我发现自己再次徘徊,所以我想我会在这里提交自己的选择。

最好的办法是查看我的博文: Timing things in Objective-C: A stopwatch

基本上,我写了一个以非常基本的方式停止观看的课程,但是封装了以便你只需要做以下事情:

[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];

你最终得到:

MyApp[4090:15203]  -> Stopwatch: [My Timer] runtime: [0.029]

在日志中......

再次,请查看我的帖子以获取更多信息或在此处下载: MMStopwatch.zip

答案 5 :(得分:7)

我使用基于Ron's解决方案的宏。

#define TICK(XXX) NSDate *XXX = [NSDate date]
#define TOCK(XXX) NSLog(@"%s: %f", #XXX, -[XXX timeIntervalSinceNow])

代码行:

TICK(TIME1);
/// do job here
TOCK(TIME1);

我们将在控制台中看到类似:TIME1:0.096618

答案 6 :(得分:6)

mach_absolute_time()有一个方便的包装器–它是CACurrentMediaTime()的功能。

  

NSDateCFAbsoluteTimeGetCurrent()偏移不同,   mach_absolute_time()CACurrentMediaTime()基于   内部主机时钟,精确的单原子测量,不受   外部时间参考的变化,例如由时间引起的变化   区域,夏令时或leap秒。


ObjC

CFTimeInterval startTime = CACurrentMediaTime();
// Do your stuff here
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(@"Total Runtime: %g s", endTime - startTime);

快速

let startTime = CACurrentMediaTime()
// Do your stuff here
let endTime = CACurrentMediaTime()
print("Total Runtime: \(endTime - startTime) s")

答案 7 :(得分:4)

我使用受code from this blog post启发的非常小的一页实现:

#import <mach/mach_time.h>

@interface DBGStopwatch : NSObject

+ (void)start:(NSString *)name;
+ (void)stop:(NSString *)name;

@end

@implementation DBGStopwatch

+ (NSMutableDictionary *)watches {
    static NSMutableDictionary *Watches = nil;
    static dispatch_once_t OnceToken;
    dispatch_once(&OnceToken, ^{
        Watches = @{}.mutableCopy;
    });
    return Watches;
}

+ (double)secondsFromMachTime:(uint64_t)time {
    mach_timebase_info_data_t timebase;
    mach_timebase_info(&timebase);
    return (double)time * (double)timebase.numer /
        (double)timebase.denom / 1e9;
}

+ (void)start:(NSString *)name {
    uint64_t begin = mach_absolute_time();
    self.watches[name] = @(begin);
}

+ (void)stop:(NSString *)name {
    uint64_t end = mach_absolute_time();
    uint64_t begin = [self.watches[name] unsignedLongLongValue];
    DDLogInfo(@"Time taken for %@ %g s",
              name, [self secondsFromMachTime:(end - begin)]);
    [self.watches removeObjectForKey:name];
}

@end

使用它非常简单:

  • 只需在开头致电[DBGStopwatch start:@"slow-operation"];
  • 完成后,
  • 然后[DBGStopwatch stop:@"slow-operation"];,以获得时间

答案 8 :(得分:3)

使用此StopWatch类,您可以获得真正精确计时(seconds.parts of seconds)。它使用iPhone中的高精度计时器。使用NSDate只能获得秒的准确性。此版本专为autorelease和objective-c设计。如果需要,我也有c ++版本。 You can find the c++ version here

<强> StopWatch.h

#import <Foundation/Foundation.h>


@interface StopWatch : NSObject 
{
    uint64_t _start;
    uint64_t _stop;
    uint64_t _elapsed;
}

-(void) Start;
-(void) Stop;
-(void) StopWithContext:(NSString*) context;
-(double) seconds;
-(NSString*) description;
+(StopWatch*) stopWatch;
-(StopWatch*) init;
@end

<强> StopWatch.m

#import "StopWatch.h"
#include <mach/mach_time.h>

@implementation StopWatch

-(void) Start
{
    _stop = 0;
    _elapsed = 0;
    _start = mach_absolute_time();
}
-(void) Stop
{
    _stop = mach_absolute_time();   
    if(_stop > _start)
    {
        _elapsed = _stop - _start;
    }
    else 
    {
        _elapsed = 0;
    }
    _start = mach_absolute_time();
}

-(void) StopWithContext:(NSString*) context
{
    _stop = mach_absolute_time();   
    if(_stop > _start)
    {
        _elapsed = _stop - _start;
    }
    else 
    {
        _elapsed = 0;
    }
    NSLog([NSString stringWithFormat:@"[%@] Stopped at %f",context,[self seconds]]);

    _start = mach_absolute_time();
}


-(double) seconds
{
    if(_elapsed > 0)
    {
        uint64_t elapsedTimeNano = 0;

        mach_timebase_info_data_t timeBaseInfo;
        mach_timebase_info(&timeBaseInfo);
        elapsedTimeNano = _elapsed * timeBaseInfo.numer / timeBaseInfo.denom;
        double elapsedSeconds = elapsedTimeNano * 1.0E-9;
        return elapsedSeconds;
    }
    return 0.0;
}
-(NSString*) description
{
    return [NSString stringWithFormat:@"%f secs.",[self seconds]];
}
+(StopWatch*) stopWatch
{
    StopWatch* obj = [[[StopWatch alloc] init] autorelease];
    return obj;
}
-(StopWatch*) init
{
    [super   init];
    return self;
}

@end

该类有一个静态stopWatch方法,它返回一个自动释放的对象。

致电start后,请使用seconds方法获取已用时间。再次致电start以重新启动它。或stop阻止它。您仍然可以在致电seconds后随时阅读时间(致电stop)。

功能中的示例(执行的定时调用)

-(void)SomeFunc
{
   StopWatch* stopWatch = [StopWatch stopWatch];
   [stopWatch Start];

   ... do stuff

   [stopWatch StopWithContext:[NSString stringWithFormat:@"Created %d Records",[records count]]];
}

答案 9 :(得分:2)

我使用此代码:

#import <mach/mach_time.h>

float TIME_BLOCK(NSString *key, void (^block)(void)) {
    mach_timebase_info_data_t info;
    if (mach_timebase_info(&info) != KERN_SUCCESS)
    {
        return -1.0;
    }

    uint64_t start = mach_absolute_time();
    block();
    uint64_t end = mach_absolute_time();
    uint64_t elapsed = end - start;

    uint64_t nanos = elapsed * info.numer / info.denom;
    float cost = (float)nanos / NSEC_PER_SEC;

    NSLog(@"key: %@ (%f ms)\n", key, cost * 1000);
    return cost;
}

答案 10 :(得分:2)

在Swift 4中使用mach_absolute_time()进行细粒度计时的示例:

let start = mach_absolute_time()

// do something

let elapsedMTU = mach_absolute_time() - start
var timebase = mach_timebase_info()
if mach_timebase_info(&timebase) == 0 {
    let elapsed = Double(elapsedMTU) * Double(timebase.numer) / Double(timebase.denom)
    print("render took \(elapsed)")
}
else {
    print("timebase error")
}

答案 11 :(得分:2)

好吧,如果您的目标是找出可以解决的问题,以使其更快,那就是一个不同的目标。测量函数所花费的时间是一个很好的方法,可以确定你所做的事情是否有所作为,但是找出要做的事情你需要一种不同的技术。 This is what I recommend,我知道你可以在iPhone上做到这一点。

编辑:审稿人建议我详细说明答案,所以我试着想出一个简短的说法。
你的整个程序需要足够的时间来打扰你。假设 N 秒 你假设你可以加快速度。你能做到这一点的唯一方法就是让它在那段时间内不做它正在做的事情,占据 m 秒。
你最初不知道那是什么东西。你可以猜测,就像所有程序员一样,但它可能很容易成为别的东西。无论是什么,这是你如何找到它:

既然那个东西,无论它是什么,都占了时间的分数 m / N ,这意味着如果你随机暂停它,概率是 m / N 那个你会在做那件事时抓住它。当然它可能正在做其他事情,但暂停它,看看它在做什么 现在再做一次。如果你再次看到它做同样的事情,你可能会更加怀疑。

做10次,或者20次。现在,如果你看到它在多次停顿时做了一些特别的事情(无论你如何形容),你可以摆脱,你知道两件事。你非常清楚地知道它花了多少时间,但你知道非常准确要解决的问题 如果您还想知道非常准确将节省多少时间,这很容易。先测量它,修复它,然后测量它。如果你真的很失望,请退出修复。

您是否看到这与测量有何不同?这是发现,而不是衡量。大多数概况分析的基础是尽可能准确地测量花费多少时间,好像这很重要,并且可以解决识别需要修复的问题的问题。分析并没有找到所有问题,但是这种方法确实找到了所有问题,而且你发现的问题不会对你造成伤害。

答案 12 :(得分:2)

我用这个:

clock_t start, end;
double elapsed;
start = clock();

//Start code to time

//End code to time

end = clock();
elapsed = ((double) (end - start)) / CLOCKS_PER_SEC;
NSLog(@"Time: %f",elapsed);

但我不确定iPhone上的CLOCKS_PER_SEC。你可能想把它关掉。

答案 13 :(得分:0)

这是另一种方法,在Swift中,使用defer关键字

来做到这一点
func methodName() {
  let methodStart = Date()
  defer {
    let executionTime = Date().timeIntervalSince(methodStart)
    print("Execution time: \(executionTime)")
  }
  // do your stuff here
}

来自Apple的docs defer语句用于在将程序控制转移到延迟语句出现的范围之外之前执行代码。

这类似于try / finally块,其优点是将相关代码分组。

答案 14 :(得分:0)

我在我的utils库( Swift 4.2 )中使用了此代码:

public class PrintTimer {
    let start = Date()
    let name: String

    public init(file: String=#file, line: Int=#line, function: String=#function, name: String?=nil) {
        let file = file.split(separator: "/").last!
        self.name = name ?? "\(file):\(line) - \(function)"
    }

    public func done() {
        let end = Date()
        print("\(self.name) took \((end.timeIntervalSinceReferenceDate - self.start.timeIntervalSinceReferenceDate).roundToSigFigs(5)) s.")
    }
}

...然后调用类似的方法:

func myFunctionCall() {
    let timer = PrintTimer()
    // ...
    timer.done()
}

...运行后,它在控制台中如下所示:

MyFile.swift:225 - myFunctionCall() took 1.8623 s.

不如上面的TICK / TOCK简洁,但是很清楚地看到它在做什么,并自动包括正在计时的时间(按文件,方法开头的行和函数名)。显然,如果我想了解更多细节(例如,如果我不像通常情况那样计时一个方法调用,而是在该方法中计时一个块),我可以在PrintTimer初始化上添加“ name =“ Foo”“参数为其命名,除了默认值。

答案 15 :(得分:-1)

struct TIME {

    static var ti = mach_timebase_info()
    static var k: Double = 1
    static var mach_stamp: Double {

        if ti.denom == 0 {
            mach_timebase_info(&ti)
            k = Double(ti.numer) / Double(ti.denom) * 1e-6
        }
        return Double(mach_absolute_time()) * k
    }
    static var stamp: Double { return NSDate.timeIntervalSinceReferenceDate() * 1000 }
}

do {
    let mach_start = TIME.mach_stamp
    usleep(200000)
    let mach_diff = TIME.mach_stamp - mach_start

    let start = TIME.stamp
    usleep(200000)
    let diff = TIME.stamp - start

    print(mach_diff, diff)
}

答案 16 :(得分:-1)

这是一个Swift 3解决方案,用于在任何地方将代码二等分以查找长时间运行的进程。

func instrument(_ title: String) {
    increment += 1
    let incrementedTime = -incrementTime.timeIntervalSinceNow
    let newPoint = Instrumentation(title, increment, incrementedTime)
    elapsedTimes.append(newPoint)
    incrementTime = NSDate()
}
instrument("View Did Appear")

print("ELAPSED TIMES \(elapsedTimes)")

用法: -

.each()

示例输出: -

  

ELAPSED TIMES [MyApp.SomeViewController.Instrumentation(标题:&#34;开始视图   Load Load&#34;,point:1,elapsedTime:0.040504038333892822),   MyApp.SomeViewController.Instrumentation(标题:&#34;完成添加   SubViews&#34;,point:2,elapsedTime:0.010585010051727295),   MyApp.SomeViewController.Instrumentation(标题:&#34;视图显示出来&#34;,   point:3,elapsedTime:0.56564098596572876)]

答案 17 :(得分:-1)

许多答案都很奇怪,并且不会以毫秒为单位给出结果(但是以秒或其他方式):

这里我用来获得MS(MILLISECONDS):

<强>夫特:

ALTER KEYSPACE "system_auth"
  WITH REPLICATION = {
    'class':'SimpleStrategy',
    'replication_factor':3
  };

<强>目标-C:

let startTime = NSDate().timeIntervalSince1970 * 1000

// your Swift code

let endTimeMinusStartTime = NSDate().timeIntervalSince1970 * 1000 - startTime
print("time code execution \(endTimeMinStartTime) ms")

答案 18 :(得分:-1)

对于Swift 4,添加为您的班级代表:

public protocol TimingDelegate: class {
    var _TICK: Date?{ get set }
}

extension TimingDelegate {
    var TICK: Date {
        _TICK = Date()
        return(_TICK)!
     }

    func TOCK(message: String)  {

        if (_TICK == nil){
            print("Call 'TICK' first!")
        }

        if (message == ""){
            print("\(Date().timeIntervalSince(_TICK!))")
        }
        else{
            print("\(message): \(Date().timeIntervalSince(_TICK!))")
        }
    }
}

加入我们的班级:

class MyViewcontroller: UIViewController, TimingDelegate

然后加入你的班级:

var _TICK: Date?

如果您想要时间,请从:

开始
TICK

结束于:

TOCK("Timing the XXX routine")

答案 19 :(得分:-1)

由于您希望在UIWebView中优化从一个页面移动到另一个页面的时间,这是否意味着您真的希望优化加载这些页面时使用的Javascript?

为此,我将看一下像这里谈到的WebKit分析器:

http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/

另一种方法是从较高级别开始,并考虑如何使用AJAX样式页面加载来设计有问题的网页以最小化加载时间,而不是每次都刷新整个webview。