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上的细粒度定时,应该使用
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"]; 得到时间
Unlike
NSDate orCFAbsoluteTimeGetCurrent() offsets,
mach_absolute_time() andCACurrentMediaTime() 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 |
类有一个静态
调用
函数中的示例(执行的定时调用)
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中使用
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 |
但我不确定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"参数,将其命名为除默认值之外的其他值。