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

How to log a method's execution time exactly in milliseconds?

是否有方法确定一个方法需要执行多少时间(毫秒)?


1
2
3
4
5
6
7
NSDate *methodStart = [NSDate date];

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

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

Swift:

1
2
3
4
5
6
7
let methodStart = NSDate()

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

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

SWIFT3:

1
2
3
4
5
6
7
let methodStart = Date()

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

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

使用方便,精度亚毫秒。


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

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

这样使用:

1
2
3
4
5
TICK;

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

TOCK;


对于OS X上的细粒度定时,应该使用中声明的mach_absolute_time( )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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的工具来分析应用程序的运行情况可能会更有用。这不会给您准确的时间信息,但它会告诉您应用程序的时间在哪里花费了多少百分比,这通常更有用(但并非总是如此)。


在Swift中,我使用:

在macros.swift中,我刚添加了

1
2
3
4
5
6
var startTime = NSDate()
func TICK(){ startTime =  NSDate() }
func TOCK(function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__){
    println("\(function) Time: \(startTime.timeIntervalSinceNow)
Line:\(line) File: \(file)"
)
}

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

1
2
3
4
5
TICK()

// your code to be tracked

TOCK()
  • 这个密码是根据罗恩的密码翻译成斯威夫特的,他有信用证
  • 我使用的是全球级别的开始日期,欢迎提出任何改进建议。


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

最好的办法是看看我的博客:在Objective-C中计时:秒表

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

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

最后你会得到:

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

在原木中…

再次,请查看我的帖子了解更多信息或在此处下载:MultExcWest.Zip


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

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

对于代码行:

1
2
3
TICK(TIME1);
/// do job here
TOCK(TIME1);

我们将在控制台中看到类似的内容:时间1:0.096618


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#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"];得到时间

mach_absolute_time()有一个方便的包装器,它是一个CACurrentMediaTime()函数。

Unlike NSDate or CFAbsoluteTimeGetCurrent() offsets,
mach_absolute_time() and CACurrentMediaTime() are based on the
internal host clock, a precise, monatomic measure, and not subject to
changes in the external time reference, such as those caused by time
zones, daylight savings, or leap seconds.

Objc

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

迅捷

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

使用这个秒表类,您可以获得非常好的计时(秒。秒的一部分)。它使用iPhone中的高精度计时器。使用nsdate只会获得第二个精度。这个版本是专为自动和Objy-C设计的。如果需要的话,我还有一个C++版本。你可以在这里找到C++版本。

秒表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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

秒表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#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来阻止它。打电话给stop后,您仍然可以随时读取时间(称为seconds)。

函数中的示例(执行的定时调用)

1
2
3
4
5
6
7
8
9
-(void)SomeFunc
{
   StopWatch* stopWatch = [StopWatch stopWatch];
   [stopWatch Start];

   ... do stuff

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


好吧,如果你的目标是找出你能解决的问题,使之更快,那是一个有点不同的目标。测量函数所花费的时间是一个很好的方法,可以找出你所做的是否有所不同,但是要找出要做什么,你需要一种不同的技术。这是我推荐的,我知道你可以在iPhone上使用。

编辑:评论家建议我详细阐述答案,所以我想用一种简短的方式来表达。你的整个计划需要足够的时间来打扰你。假设这是N秒。你假设你能加快速度。你唯一能做到的方法就是让它不做它在那个时间里正在做的事情,占M秒。你一开始不知道那是什么。你可以猜到,就像所有的程序员一样,但它很容易成为其他东西。不管是什么,你都可以找到它:

因为不管它是什么,它占时间的m/n的分数,这意味着如果你随机地暂停它,你在做那件事的时候,会捕捉到它的概率是m/n。当然,这可能是在做别的事情,但是暂停一下,看看它在做什么。现在再做一次。如果你看到它再次做同样的事情,你可能会更加怀疑。

做10次,或者20次。现在,如果你看到它在多次停顿中做一些特殊的事情(不管你如何描述),你可以摆脱,你知道两件事。你很粗略地知道它需要花费多少时间,但你很准确地知道要解决什么问题。如果你还想非常准确地知道将节省多少时间,那很容易。先量,后修。如果你真的很失望,那就退出修复。

你看到这和测量有什么不同吗?它是发现,而不是测量。大多数的分析都是基于尽可能精确地测量花费了多少时间,就好像这很重要一样,然后用手挥动确定需要解决的问题。分析并不能找到所有的问题,但是这个方法确实能找到所有的问题,而你没有发现的问题却伤害了你。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
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")
}

我用这个:

1
2
3
4
5
6
7
8
9
10
11
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上的时钟是多少。你可能想把它忘了。


我使用此代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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)
"
, key, cost * 1000);
    return cost;
}

这里有另一种方法,在swift中,使用defer关键字

1
2
3
4
5
6
7
8
func methodName() {
  let methodStart = Date()
  defer {
    let executionTime = Date().timeIntervalSince(methodStart)
    print("Execution time: \(executionTime)")
  }
  // do your stuff here
}

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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)
}

这里有一个快速的3解决方案,可以将代码分割到任何地方,以找到一个长期运行的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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]()
1
2
3
4
5
6
7
func instrument(_ title: String) {
    increment += 1
    let incrementedTime = -incrementTime.timeIntervalSinceNow
    let newPoint = Instrumentation(title, increment, incrementedTime)
    elapsedTimes.append(newPoint)
    incrementTime = NSDate()
}

用途:

1
2
3
instrument("View Did Appear")

print("ELAPSED TIMES \(elapsedTimes)")

样品输出:

ELAPSED TIMES [MyApp.SomeViewController.Instrumentation(title:"Start View
Did Load", point: 1, elapsedTime: 0.040504038333892822),
MyApp.SomeViewController.Instrumentation(title:"Finished Adding
SubViews", point: 2, elapsedTime: 0.010585010051727295),
MyApp.SomeViewController.Instrumentation(title:"View Did Appear",
point: 3, elapsedTime: 0.56564098596572876)]


许多答案都很奇怪,并不能以毫秒为单位给出结果(但以秒或其他任何形式):

这里是我用来得到ms(毫秒)的:

Swift:

1
2
3
4
5
6
let startTime = NSDate().timeIntervalSince1970 * 1000

// your Swift code

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

ObjultC:

1
2
3
4
5
6
7
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
"
, endTimeMinusStartTime );

对于Swift 4,作为学员添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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!))")
        }
    }
}

加入我们的课程:

1
class MyViewcontroller: UIViewController, TimingDelegate

然后添加到您的类中:

1
var _TICK: Date?

当你想计时时,从以下开始:

1
TICK

并以:

1
TOCK("Timing the XXX routine")


既然您想优化uiwebview中从一个页面移动到另一个页面的时间,这不意味着您真的想要优化加载这些页面时使用的javascript吗?

为此,我将看一个Webkit事件探查器,如这里所说:

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

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


我在我的实用程序库中使用这个(swift 4.2):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.")
    }
}

…然后调用如下方法:

1
2
3
4
5
func myFunctionCall() {
    let timer = PrintTimer()
    // ...
    timer.done()
}

…运行后,在控制台中会出现这样的情况:

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

不如上面的tick/tock简洁,但它足够清晰,可以看到它正在做什么,并自动包括正在计时的内容(通过文件、方法开头的行和函数名)。显然,如果我想要更详细的信息(例如,如果我不只是按照通常的情况计时一个方法调用,而是在该方法中计时一个块),我可以在printTimer init上添加"name="foo"参数,将其命名为除默认值之外的其他值。