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


222

有没有一种方法可以确定一个方法需要执行多少时间(以毫秒为单位)?


2
您是否因为想找出可以优化以使其更快而优化的东西而问?
Mike Dunlavey 2010年

1
是的,我正在使用正在加载某些页面的UIWebView。我想通过检查方法需要加载页面1时至10页优化pageloading

2
这似乎是这个问题的一个副本:stackoverflow.com/questions/889380/...
布拉德·拉尔森

@BradLarson尽管它似乎是重复的,但另一个问题的答案更好,即,主要的答案不是建议使用(错误的)NSDate,而是很好地解释了为什么NSDate是用于此目的的错误方法。
Thomas Tempelmann

Answers:


437
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)")

易于使用,并具有亚毫秒级的精度。


3
@PeterWarbo NSTimeInterval是double的typedef,并且定义为秒-参见developer.apple.com/library/mac/#documentation/Cocoa/Reference/…–
Ben

5
您可以使用%f记录此值-NSLog(“ executionTime =%f”,executeTime);
托尼

1
@Tony,您忘了@,NSLog(@"executionTime = %f", executionTime);
John Riselvato

6
我只是比较NSDatemach_absolute_time()大约30毫秒的水平。27 vs. 29、36 vs. 39、43 vs. 45. NSDate对我来说更容易使用,结果也足够相似,不会被困扰mach_absolute_time()
2013年

5
任何基于NSDate的内容都不适合测量经过的时间,因为时间可能会跳跃甚至倒退。一种更安全的方法是使用mach_absolute_time,如此处其他许多答案所示。这是一个不好的例子,应该被否决。另请参阅相关答案,以更详细地解释了所有这些:stackoverflow.com/a/30363702/43615
Thomas Tempelmann

252

这是我使用的两个单行宏:

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

像这样使用它:

TICK;

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

TOCK;

13
哈哈。我喜欢!
bobmoff

5
之所以如此出色,是因为滴答滴答是一个令人难忘的短语,日志记录几乎不需要思考。
约翰·里塞尔瓦托

30
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])使这个答案还返回使用计时器的功能。如果我使用TICK TOCK对多个功能进行计时,则这很有用。
golmschenk 2014年

3
好主意@golmschenk!您也可以调查__PRETTY_FUNCTION__以及__LINE__是否需要更详细的信息。
罗恩2014年

50

为了在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之​​类的工具来分析运行的应用程序可能会更有用。这不会为您提供确切的计时信息,但会告诉您应用程序在哪里花费的时间百分比,这通常更有用(但并非总是如此)。


1
试图让它在Swift中工作...有什么建议吗?
zumzum 2015年

1
“一个人并不仅仅是...转换为Swift”
-Ned

@zumzum有关在Swift中执行此操作的示例,请参见我的答案。
jbg

23

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

不像NSDateCFAbsoluteTimeGetCurrent()偏移, mach_absolute_time()CACurrentMediaTime()基于该内部主机时钟,精确的,单原子的措施,而不是在外部时间基准的变化,例如那些引起的时区,夏令时,或闰秒主题。


对象

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")

3
我认为这个答案值得更多的赞同。比使用更好NSDate
平均乔

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()
  • 这段代码是基于罗恩翻译成Swift的代码,
  • 我在全球范围内使用开始日期,欢迎提出任何改进建议

这应该是\(-startTime.timeIntervalSinceNow)(注意负面消息)
雪人

9

我知道这是一个古老的游戏,但是即使我发现自己又一次徘徊,所以我想在这里提交自己的选择。

最好的选择是查看我的博客文章: Objective-C中的计时:秒表

基本上,我编写了一个类,该类确实以一种非常基本的方式停止监视,但是已封装好了,因此您只需要执行以下操作:

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

结果是:

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

在日志中...

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


7

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

#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


您的答案与罗恩(Ron)的答案并没有太大区别,而且我也莫名其妙地看到哪种方法更好?
Trilarion '16

2
您不能在一个上下文中两次使用@Ron解决方案。这是使用此宏的主要原因。
Sergey Teryokhin,2016年

4

我使用了非常少的一页类实现,其灵感来自于 来自此博客文章中的代码

#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"];完成之后,抽出时间

3

使用此StopWatch类,您可以获得非常好的计时(seconds.seconds)。它使用iPhone中的高精度计时器。使用NSDate只会使您获得秒精度。此版本专门为自动发布和Objective-C设计。如果需要,我也有C ++版本。 您可以在此处找到c ++版本

秒表

#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

秒表

#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]]];
}

您的“仅秒精度”不正确。虽然NSTimeInterval的整个部分为秒,但它是两倍。
史蒂芬·费舍尔

3

我使用以下代码:

#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;
}

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。您可能要关闭它。


2
iPhone上的CLOCKS_PER_SEC是一个非常不准确的值。
mxcl 2011年

1
很高兴知道。如果我现在必须这样做,我会用Matthew的答案。
David Kanarek

2

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

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")
}

2

好的,如果您的目标是找出可以解决的问题以使其更快,那是有点不同的目标。测量功能所花费的时间是一种很好的方法,可以确定所做的事情是否有所作为,但是要确定要做什么,您需要使用其他技术。这是我推荐的,我知道您可以在iPhone上做到这一点。

编辑:审阅者建议我详细说明答案,所以我试图考虑一种简短的说法。
您的整个程序需要足够的时钟时间来打扰您。假设那是N秒。
您假设可以加快速度。您可以执行此操作的唯一方法是使其不执行该操作,这仅占m秒。
您最初不知道那是什么。您可以像所有程序员一样猜测,但这很容易是另外一回事。无论是什么,您都可以在这里找到它:

既然那个东西,无论是什么,都占时间的m / N,这意味着如果您随机将其暂停,则执行该事情的概率为m / N。当然,它可能正在做其他事情,但是请暂停它,然后看看它在做什么。
现在再做一次。如果您看到它再次做同样的事情,您可能会更加怀疑。

进行10次或20次。现在,如果您看到它在多个暂停中执行某些特定的操作(无论如何描述),则可以摆脱,您知道两件事。您大概知道需要多少时间,但是却确切知道要解决什么。
如果您还想确切地知道将节省多少时间,那很容易。先测量,然后修复,然后再测量。如果您真的很失望,请退出此修复程序。

您看到这与测量有何不同?它是发现,而不是测量。大多数性能分析是基于尽可能精确地测量花费了多少时间(似乎很重要),并且动手解决了确定需要修复的问题。分析并不能找到所有问题,但是此方法可以找到所有问题,而您发现的问题并没有伤害您。


0

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

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

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

这类似于try / finally块,但具有将相关代码分组的优点。


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”“参数除了默认值以外,还要为其命名。


-1

既然您要优化UIWebView中从一页移动到另一页的时间,这是否就意味着您真的要优化加载这些页面所使用的Javascript?

为此,我将看一下此处讨论过的WebKit分析器:

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

另一种方法是从较高的层次开始,并思考如何使用AJAX样式页面加载而不是每次刷新整个Webview来设计有问题的网页,以最大程度地减少加载时间。


-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)
}

-1

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

var increment: Int = 0

var incrementTime = NSDate()

struct Instrumentation {
    var title: String
    var point: Int
    var elapsedTime: Double

    init(_ title: String, _ point: Int, _ elapsedTime: Double) {
        self.title = title
        self.point = point
        self.elapsedTime = elapsedTime
    }
}

var elapsedTimes = [Instrumentation]()

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)")

样本输出:

有效时间[MyApp.SomeViewController.Instrumentation(标题:“开始加载视图”,点:1,经过时间:0.040504038333892822),MyApp.SomeViewController.Instrumentation(标题:“完成添加子视图”,点:2,经过时间:0.010585010051727295), MyApp.SomeViewController.Instrumentation(标题:“出现了视图”,指向:3,经过时间:0.56564098596572876)]


-1

许多答案很怪异,实际上并没有以毫秒为单位给出结果(而是以秒或其他形式给出):

这是我用来获取MS(MILLISECONDS)的方法:

迅速:

let startTime = NSDate().timeIntervalSince1970 * 1000

// your Swift code

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

目标C:

double startTime = [[NSDate date] timeIntervalSince1970] * 1000.0;

// your Objective-C code

double endTimeMinusStartTime = [[NSDate date] timeIntervalSince1970] * 1000.0 - startTime;
printf("time code execution %f ms\n", endTimeMinusStartTime );

-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")

您是否阅读了答案和评论?请勿为此使用日期!
马特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.