有没有一种方法可以确定一个方法需要执行多少时间(以毫秒为单位)?
有没有一种方法可以确定一个方法需要执行多少时间(以毫秒为单位)?
Answers:
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)")
易于使用,并具有亚毫秒级的精度。
NSLog(@"executionTime = %f", executionTime);
NSDate
和mach_absolute_time()
大约30毫秒的水平。27 vs. 29、36 vs. 39、43 vs. 45. NSDate
对我来说更容易使用,结果也足够相似,不会被困扰mach_absolute_time()
。
这是我使用的两个单行宏:
#define TICK NSDate *startTime = [NSDate date]
#define TOCK NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])
像这样使用它:
TICK;
/* ... Do Some Work Here ... */
TOCK;
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])
使这个答案还返回使用计时器的功能。如果我使用TICK TOCK对多个功能进行计时,则这很有用。
__PRETTY_FUNCTION__
以及__LINE__
是否需要更详细的信息。
为了在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之类的工具来分析运行的应用程序可能会更有用。这不会为您提供确切的计时信息,但会告诉您应用程序在哪里花费的时间百分比,这通常更有用(但并非总是如此)。
有一个方便的包装器mach_absolute_time()
–它是一个CACurrentMediaTime()
功能。
不像
NSDate
或CFAbsoluteTimeGetCurrent()
偏移,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")
NSDate
。
在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()
\(-startTime.timeIntervalSinceNow)
(注意负面消息)
我知道这是一个古老的游戏,但是即使我发现自己又一次徘徊,所以我想在这里提交自己的选择。
最好的选择是查看我的博客文章: Objective-C中的计时:秒表
基本上,我编写了一个类,该类确实以一种非常基本的方式停止监视,但是已封装好了,因此您只需要执行以下操作:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
结果是:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
在日志中...
同样,请查看我的帖子以获取更多信息或在此处下载: MMStopwatch.zip
我使用基于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
我使用了非常少的一页类实现,其灵感来自于 来自此博客文章中的代码:
#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"];
完成之后,抽出时间使用此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]]];
}
我使用以下代码:
#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;
}
我用这个:
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。您可能要关闭它。
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")
}
好的,如果您的目标是找出可以解决的问题以使其更快,那是有点不同的目标。测量功能所花费的时间是一种很好的方法,可以确定所做的事情是否有所作为,但是要确定要做什么,您需要使用其他技术。这是我推荐的,我知道您可以在iPhone上做到这一点。
编辑:审阅者建议我详细说明答案,所以我试图考虑一种简短的说法。
您的整个程序需要足够的时钟时间来打扰您。假设那是N秒。
您假设可以加快速度。您可以执行此操作的唯一方法是使其不执行该操作,这仅占m秒。
您最初不知道那是什么。您可以像所有程序员一样猜测,但这很容易是另外一回事。无论是什么,您都可以在这里找到它:
既然那个东西,无论是什么,都占时间的m / N,这意味着如果您随机将其暂停,则执行该事情的概率为m / N。当然,它可能正在做其他事情,但是请暂停它,然后看看它在做什么。
现在再做一次。如果您看到它再次做同样的事情,您可能会更加怀疑。
进行10次或20次。现在,如果您看到它在多个暂停中执行某些特定的操作(无论如何描述),则可以摆脱,您知道两件事。您大概知道需要多少时间,但是却确切知道要解决什么。
如果您还想确切地知道将节省多少时间,那很容易。先测量,然后修复,然后再测量。如果您真的很失望,请退出此修复程序。
您看到这与测量有何不同?它是发现,而不是测量。大多数性能分析是基于尽可能精确地测量花费了多少时间(似乎很重要),并且动手解决了确定需要修复的问题。分析并不能找到所有问题,但是此方法可以找到所有问题,而您发现的问题并没有伤害您。
在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块,但具有将相关代码分组的优点。
我在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”“参数除了默认值以外,还要为其命名。
既然您要优化UIWebView中从一页移动到另一页的时间,这是否就意味着您真的要优化加载这些页面所使用的Javascript?
为此,我将看一下此处讨论过的WebKit分析器:
http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/
另一种方法是从较高的层次开始,并思考如何使用AJAX样式页面加载而不是每次刷新整个Webview来设计有问题的网页,以最大程度地减少加载时间。
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)
}
这是一个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)]
许多答案很怪异,实际上并没有以毫秒为单位给出结果(而是以秒或其他形式给出):
这是我用来获取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 );
对于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")