如何精确地以毫秒为单位记录一个方法的执行时间?

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

109511 次浏览

好吧,如果你的目标是找出你可以修复什么使它更快,那是一个有点不同的目标。测量函数所花费的时间是发现你所做的是否有影响的好方法,但对于找出该怎么做你需要不同的技术。这就是我的建议,我知道你可以在iphone上做。

编辑:审稿人建议我详细解释一下答案,所以我正在想一个简单的方式来解释 你的整个程序需要足够的时钟时间来打扰你。假设是N seconds.
你假设你可以加速它。你能做到这一点的唯一方法是让它在该时间内不做它正在做的事情,占秒 你一开始并不知道这个东西是什么。您可以像所有程序员一样猜测,但它很容易是其他东西。不管它是什么,下面是你如何找到它:

因为那个东西,不管它是什么,占据了部分m / N的时间,这意味着如果你随机暂停它,你在做那件事的时候抓住它的概率是m / N。当然,它可能在做其他事情,但是暂停它,看看它在做什么 现在再做一次。如果你看到它再次做同样的事情,你就更可疑了 做10次,或者20次。现在,如果你看到它在多次暂停中做一些特定的事情(不管你怎么描述它),你可以摆脱,你知道两件事。你大概知道它需要多少时间,但是你知道非常准确的要修复什么 如果你还想知道非常准确的将节省多少时间,这很简单。之前测量,修正,之后测量。如果你真的很失望,就退出修复。

你知道这和测量有什么不同吗?发现,而不是测量。大多数分析都是基于尽可能精确地测量所花费的时间,就好像这很重要一样,并明确需要解决的问题。剖析并不能找到所有的问题,但是这种方法确实能找到所有的问题,而那些你没有找到的问题对你造成了伤害。

我用这个:

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。你还是别打了吧。

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

#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。这不会给你确切的计时信息,但它会告诉你应用程序时间的百分比在哪里花费,这通常更有用(但并不总是如此)。

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

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

既然你想优化时间从一个页面移动到另一个UIWebView,这是不是意味着你真的在寻找优化Javascript加载这些页面?

为此,我想看看WebKit分析器,就像这里所说的:

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

另一种方法是从高层次开始,思考如何设计有问题的网页,使用AJAX样式的页面加载来最小化加载时间,而不是每次都刷新整个web视图。

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

最好的办法是看看我的博客文章: 在Objective-C中计时:秒表 < / p >

基本上,我写了一个类,它以非常基本的方式停止观看,但被封装,所以你只需要做以下事情:

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

最后你会得到:

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

在日志里…

再说一次,看看我的帖子,或者在这里下载: MMStopwatch.zip < / p >

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

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

像这样使用它:

TICK;


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


TOCK;

你可以得到真的精细计时(秒。部分秒)使用这个StopWatch类。它使用了iPhone的高精度计时器。使用NSDate只能获得秒级精度。这个版本是专门为自动发布和objective-c设计的。如果需要的话,我也有一个c++版本。你可以在这里找到c++版本

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来停止它。在调用stop之后,仍然可以在任何时候读取时间(调用seconds)。

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

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


... do stuff


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

我使用了非常小的,一页的类实现,灵感来自这篇博文中的代码:

#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"];,以获得时间

在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()

斯威夫特5.0

   var startTime = NSDate()
func TICK(){ startTime =  NSDate() }
func TOCK(function: String = #function, file: String = #file, line: Int = #line){
print("\(function) Time: \(startTime.timeIntervalSinceNow)\nLine:\(line) File: \(file)")
}
  • 这段代码是基于Ron的代码翻译成Swift的,他有功劳
  • 我在全球范围内使用开始日期,任何改进建议都是欢迎的

我使用这个代码:

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

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

#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

这里有另一种方法,在Swift中,使用defer关键字来做到这一点

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

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

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

这里有一个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)")

样例输出:

ELAPSED TIMES [MyApp.SomeViewController.]仪器仪表(标题:“开始视图 Did Load", point: 1, elapsedTime: 0.040504038333892822), MyApp.SomeViewController。检测(标题:“完成添加 SubViews", point: 2, elapsedTime: 0.010585010051727295), MyApp.SomeViewController。仪器仪表(标题:“视图已出现”, point: 3, elapsedTime: 0.56564098596572876)]

许多答案很奇怪,并没有真正以毫秒为单位给出结果(而是以秒或其他单位):

这里是我用来获得MS(毫秒):

迅速:

let startTime = NSDate().timeIntervalSince1970 * 1000


// your Swift code


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

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

我在utils库(斯威夫特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 init上添加“name="Foo""参数来命名它除了默认值之外的东西。

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

mach_absolute_time()有一个方便的包装器——它是CoreAnimation框架中的CACurrentMediaTime()函数。

NSDateCFAbsoluteTimeGetCurrent()偏移量不同, mach_absolute_time()CACurrentMediaTime()基于 主机内部时钟,精确,单原子测量,且不受影响 外部时间参考的更改,例如由时间引起的更改

.

.

.

ObjC

#import <QuartzCore/QuartzCore.h>


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

斯威夫特

import QuartzCore


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