Existe uma maneira de determinar quanto tempo um método precisa executar (em milissegundos)?
Existe uma maneira de determinar quanto tempo um método precisa executar (em milissegundos)?
Respostas:
NSDate *methodStart = [NSDate date];
/* ... Do whatever you need to do ... */
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);
Rápido:
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)")
Fácil de usar e com precisão abaixo de milissegundos.
NSLog(@"executionTime = %f", executionTime);
NSDate
e mach_absolute_time()
em torno de 30ms. 27 x 29, 36 x 39, 43 x 45. NSDate
foi mais fácil de usar e os resultados foram semelhantes o suficiente para não incomodar mach_absolute_time()
.
Aqui estão duas macros de uma linha que eu uso:
#define TICK NSDate *startTime = [NSDate date]
#define TOCK NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])
Use-o assim:
TICK;
/* ... Do Some Work Here ... */
TOCK;
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])
faz com que esta resposta também retorne em qual função o temporizador foi usado. Achei isso útil se eu usasse o TICK TOCK para cronometrar várias funções.
__PRETTY_FUNCTION__
e __LINE__
se deseja informações mais detalhadas.
Para um tempo mais refinado no OS X, você deve usar mach_absolute_time( )
declarado em <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;
É claro que as advertências usuais sobre medições refinadas se aplicam; provavelmente é melhor invocar a rotina em teste várias vezes e calcular a média / fazer um mínimo / outra forma de processamento.
Além disso, observe que você pode achar mais útil criar um perfil do aplicativo em execução usando uma ferramenta como o Shark. Isso não fornecerá informações exatas sobre o tempo, mas informará qual porcentagem do tempo do aplicativo está sendo gasta onde, o que geralmente é mais útil (mas nem sempre).
Existe um invólucro conveniente para mach_absolute_time()
- é uma CACurrentMediaTime()
função.
Diferentemente
NSDate
ouCFAbsoluteTimeGetCurrent()
compensados,mach_absolute_time()
eCACurrentMediaTime()
são baseados no relógio interno do host, uma medida precisa e monatômica, e não estão sujeitos a alterações na referência de horário externo, como as causadas por fusos horários, horário de verão ou segundos bissextos.
ObjC
CFTimeInterval startTime = CACurrentMediaTime();
// Do your stuff here
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(@"Total Runtime: %g s", endTime - startTime);
Rápido
let startTime = CACurrentMediaTime()
// Do your stuff here
let endTime = CACurrentMediaTime()
print("Total Runtime: \(endTime - startTime) s")
NSDate
.
No Swift, estou usando:
No meu Macros.swift acabei de adicionar
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)")
}
agora você pode simplesmente ligar para qualquer lugar
TICK()
// your code to be tracked
TOCK()
\(-startTime.timeIntervalSinceNow)
(notar o negativo)
Sei que é antiga, mas até me vi vagando novamente, então pensei em enviar minha própria opção aqui.
A melhor aposta é dar uma olhada no meu blog: Cronometrando as coisas no Objective-C: Um cronômetro
Basicamente, escrevi uma classe que para de assistir de uma maneira muito básica, mas é encapsulada para que você só precise fazer o seguinte:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
E você acaba com:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
no log ...
Mais uma vez, confira meu post um pouco mais ou faça o download aqui: MMStopwatch.zip
Eu uso macros baseadas na solução de Ron .
#define TICK(XXX) NSDate *XXX = [NSDate date]
#define TOCK(XXX) NSLog(@"%s: %f", #XXX, -[XXX timeIntervalSinceNow])
Para linhas de código:
TICK(TIME1);
/// do job here
TOCK(TIME1);
veremos no console algo como: TIME1: 0.096618
Eu uso uma implementação de classe de página muito mínima, inspirada no código desta postagem do blog :
#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
O uso dele é muito simples:
[DBGStopwatch start:@"slow-operation"];
no começo[DBGStopwatch stop:@"slow-operation"];
depois do final, para ganhar tempoVocê pode obter um timing muito bom (segundos.parts de segundos) usando esta classe StopWatch. Ele usa o timer de alta precisão no iPhone. O uso do NSDate fornece apenas uma precisão de segundo (s). Esta versão foi projetada especificamente para liberação automática e objetivo-c. Eu tenho uma versão c ++, bem se necessário. Você pode encontrar a versão c ++ aqui .
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
A classe possui um stopWatch
método estático que retorna um objeto liberado automaticamente.
Depois de ligar start
, use o seconds
método para obter o tempo decorrido. Ligue start
novamente para reiniciá-lo. Ou stop
para pará-lo. Você ainda pode ler a hora (chamada seconds
) a qualquer momento após a chamada stop
.
Exemplo em uma função (chamada de tempo de execução)
-(void)SomeFunc
{
StopWatch* stopWatch = [StopWatch stopWatch];
[stopWatch Start];
... do stuff
[stopWatch StopWithContext:[NSString stringWithFormat:@"Created %d Records",[records count]]];
}
Eu uso este código:
#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;
}
Eu uso isso:
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);
Mas não tenho certeza sobre CLOCKS_PER_SEC no iPhone. Você pode querer deixar de fora.
Um exemplo de tempo refinado usando mach_absolute_time()
no 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")
}
OK, se seu objetivo é descobrir o que você pode corrigir para torná-lo mais rápido, esse é um objetivo um pouco diferente. Medir o tempo que as funções levam é uma boa maneira de descobrir se o que você fez fez a diferença, mas para descobrir o que fazer, você precisa de uma técnica diferente. É isso que eu recomendo e sei que você pode fazer isso em iPhones.
Edit: Os revisores sugeriram que eu elaborasse a resposta, então estou tentando pensar em uma maneira breve de dizê-la.
Seu programa geral leva tempo suficiente para incomodá-lo. Suponha que sejam N segundos.
Você está assumindo que pode acelerar. A única maneira de fazer isso é fazê-lo não fazer algo que está fazendo naquele tempo, contabilizando m segundos.
Você inicialmente não sabe o que é isso. Você pode adivinhar, como todos os programadores, mas poderia ser facilmente outra coisa. Seja o que for, veja como você pode encontrá-lo:
Como essa coisa, seja o que for, é responsável pela fração m / N do tempo, isso significa que, se você pausar aleatoriamente, a probabilidade é m / N de que você a capturará no ato de fazer a coisa. Claro que pode estar fazendo outra coisa, mas faça uma pausa e veja o que está fazendo.
Agora faça de novo. Se você vê fazendo a mesma coisa novamente, pode ficar mais desconfiado.
Faça 10 vezes ou 20. Agora, se você vê algo em particular (não importa como você o descreve) em várias pausas, das quais você pode se livrar, você sabe duas coisas. Você sabe muito bem qual é a fração do tempo que leva, mas sabe exatamente o que consertar.
Se você também quer saber exatamente quanto tempo será economizado, isso é fácil. Meça antes, corrija e depois. Se você está realmente desapontado, volte à solução.
Você vê como isso é diferente de medir? É encontrar, não medir . A maioria dos perfis baseia-se em medir o mais exatamente possível quanto tempo é necessário, como se isso fosse importante, e acenou com mão o problema de identificar o que precisa ser corrigido. A criação de perfil não encontra todos os problemas, mas esse método encontra todos os problemas, e são os problemas que você não acha que o machucam.
Aqui está outra maneira, no Swift, de fazer isso usando a palavra-chave adiar
func methodName() {
let methodStart = Date()
defer {
let executionTime = Date().timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")
}
// do your stuff here
}
Dos documentos da Apple : Uma declaração defer é usada para executar código imediatamente antes de transferir o controle do programa para fora do escopo em que a declaração defer aparece.
Isso é semelhante a um bloco try / finalmente com a vantagem de ter o código relacionado agrupado.
Eu uso isso na minha biblioteca de utilitários ( 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.")
}
}
... então chame um método como:
func myFunctionCall() {
let timer = PrintTimer()
// ...
timer.done()
}
... que por sua vez se parece com isso no console após a execução:
MyFile.swift:225 - myFunctionCall() took 1.8623 s.
Não é tão conciso quanto o TICK / TOCK acima, mas é claro o suficiente para ver o que está fazendo e inclui automaticamente o que está sendo cronometrado (por arquivo, linha no início do método e nome da função). Obviamente, se eu quisesse mais detalhes (por exemplo, se eu não estiver apenas cronometrando uma chamada de método, como é o caso usual, mas estiver cronometrando um bloco nesse método), posso adicionar o parâmetro "name =" Foo "" no init PrintTimer para nomear algo além dos padrões.
Como você deseja otimizar o tempo passando de uma página para outra em um UIWebView, isso não significa que você realmente deseja otimizar o Javascript usado no carregamento dessas páginas?
Para esse fim, eu examinaria um criador de perfil do WebKit como o mencionado aqui:
http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/
Outra abordagem seria começar em um nível alto e pensar em como você pode projetar as páginas da Web em questão para minimizar o tempo de carregamento usando o carregamento de páginas no estilo AJAX, em vez de atualizar a visualização da Web toda a cada vez.
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)
}
Aqui está uma solução Swift 3 para dividir o código em qualquer lugar e encontrar um processo demorado.
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()
}
Uso: -
instrument("View Did Appear")
print("ELAPSED TIMES \(elapsedTimes)")
Saída de amostra: -
TIMES ELAPSED [MyApp.SomeViewController.Instrumentation (título: "A exibição inicial foi carregada", ponto: 1, decorridoTime: 0.040504038333892822), MyApp.SomeViewController.Instrumentation (título: "Finalização da adição de sub-visualizações", ponto: 2, tempo decorrido: 0.010585010051727) MyApp.SomeViewController.Instrumentation (título: "A exibição apareceu", ponto: 3, tempo decorrido: 0.56564098596572876)]
muitas respostas são estranhas e não dão resultado em milissegundos (mas em segundos ou qualquer outra coisa):
aqui o que eu uso para obter MS (MILLISECONDS):
Rápido:
let startTime = NSDate().timeIntervalSince1970 * 1000
// your Swift code
let endTimeMinusStartTime = NSDate().timeIntervalSince1970 * 1000 - startTime
print("time code execution \(endTimeMinStartTime) ms")
Objetivo-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 );
Para o Swift 4, adicione como Delegado à sua turma:
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!))")
}
}
}
Adicione à nossa turma:
class MyViewcontroller: UIViewController, TimingDelegate
Em seguida, adicione à sua turma:
var _TICK: Date?
Quando você quiser cronometrar alguma coisa, comece com:
TICK
E termine com:
TOCK("Timing the XXX routine")