C'è un modo per determinare quanto tempo deve essere eseguito un metodo (in millisecondi)?
C'è un modo per determinare quanto tempo deve essere eseguito un metodo (in millisecondi)?
Risposte:
NSDate *methodStart = [NSDate date];
/* ... Do whatever you need to do ... */
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);
Swift:
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)")
Facile da usare e con precisione inferiore al millisecondo.
NSLog(@"executionTime = %f", executionTime);
NSDate
e mach_absolute_time()
ad un livello di circa 30 ms. 27 contro 29, 36 contro 39, 43 contro 45. NSDate
era più facile da usare per me e i risultati erano abbastanza simili da non disturbare mach_absolute_time()
.
Ecco due macro a una riga che utilizzo:
#define TICK NSDate *startTime = [NSDate date]
#define TOCK NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])
Usalo in questo modo:
TICK;
/* ... Do Some Work Here ... */
TOCK;
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])
fa sì che questa risposta ritorni anche in quale funzione è stato utilizzato il timer. L'ho trovato utile se ho usato TICK TOCK per cronometrare più funzioni.
__PRETTY_FUNCTION__
e __LINE__
se desideri informazioni più dettagliate.
Per un tempismo dettagliato su OS X, è necessario utilizzare mach_absolute_time( )
dichiarato in <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;
Naturalmente si applicano le solite avvertenze sulle misurazioni a grana fine; probabilmente è meglio invocare la routine sottoposta a test molte volte e fare la media / prendere un minimo / qualche altra forma di elaborazione.
Inoltre, tieni presente che potresti trovare più utile profilare la tua applicazione in esecuzione utilizzando uno strumento come Shark. Questo non ti fornirà informazioni esatte sui tempi, ma ti dirà quale percentuale del tempo dell'applicazione viene speso dove, che è spesso più utile (ma non sempre).
C'è un comodo wrapper per mach_absolute_time()
- è una CACurrentMediaTime()
funzione.
A differenza
NSDate
oCFAbsoluteTimeGetCurrent()
offset,mach_absolute_time()
eCACurrentMediaTime()
si basano sull'orologio host interno, una misura monatomica precisa e non sono soggetti a cambiamenti nel riferimento orario esterno, come quelli causati da fusi orari, ora legale o secondi saltanti.
objC
CFTimeInterval startTime = CACurrentMediaTime();
// Do your stuff here
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(@"Total Runtime: %g s", endTime - startTime);
veloce
let startTime = CACurrentMediaTime()
// Do your stuff here
let endTime = CACurrentMediaTime()
print("Total Runtime: \(endTime - startTime) s")
NSDate
.
In Swift, sto usando:
Nel mio Macros.swift ho appena aggiunto
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)")
}
ora puoi semplicemente chiamare ovunque
TICK()
// your code to be tracked
TOCK()
\(-startTime.timeIntervalSinceNow)
(notare il negativo)
So che questo è vecchio ma anche io mi sono ritrovato a vagare di nuovo, quindi ho pensato di presentare la mia opzione qui.
La cosa migliore è controllare il mio post sul blog su questo: Timing cose in Objective-C: un cronometro
Fondamentalmente, ho scritto una lezione che smette di guardare in un modo molto semplice ma è incapsulata in modo che tu debba solo fare quanto segue:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
E finisci con:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
nel registro ...
Ancora una volta, controlla il mio post per un po 'di più o scaricalo qui: MMStopwatch.zip
Uso macro basate sulla soluzione di Ron .
#define TICK(XXX) NSDate *XXX = [NSDate date]
#define TOCK(XXX) NSLog(@"%s: %f", #XXX, -[XXX timeIntervalSinceNow])
Per righe di codice:
TICK(TIME1);
/// do job here
TOCK(TIME1);
vedremo in console qualcosa come: TIME1: 0.096618
Uso un'implementazione di una classe di pagine molto minimale ispirata al codice di questo post del 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
Il suo utilizzo è molto semplice:
[DBGStopwatch start:@"slow-operation"];
all'inizio[DBGStopwatch stop:@"slow-operation"];
dopo il traguardo, per ottenere il tempoÈ possibile ottenere un tempismo davvero preciso (secondi.parti di secondi) usando questa classe StopWatch. Utilizza il timer ad alta precisione nell'iPhone. L'uso di NSDate ti darà solo la precisione dei secondi. Questa versione è progettata specificamente per il rilascio automatico e la versione obiettiva-c. Ho anche una versione c ++ se necessario. Puoi trovare la versione c ++ qui .
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
La classe ha un stopWatch
metodo statico che restituisce un oggetto rilasciato automaticamente.
Una volta chiamato start
, utilizzare il seconds
metodo per ottenere il tempo trascorso. Chiamare di start
nuovo per riavviarlo. O stop
per fermarlo. Puoi ancora leggere l'ora (chiamare seconds
) in qualsiasi momento dopo aver chiamato stop
.
Esempio in una funzione (chiamata di esecuzione temporizzata)
-(void)SomeFunc
{
StopWatch* stopWatch = [StopWatch stopWatch];
[stopWatch Start];
... do stuff
[stopWatch StopWithContext:[NSString stringWithFormat:@"Created %d Records",[records count]]];
}
Io uso questo codice:
#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;
}
Io lo uso questo:
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);
Ma non sono sicuro di CLOCKS_PER_SEC su iPhone. Potresti voler lasciarlo fuori.
Un esempio di temporizzazione a grana fine usando mach_absolute_time()
in 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 il tuo obiettivo è scoprire cosa puoi sistemare per renderlo più veloce, è un obiettivo leggermente diverso. Misurare il tempo impiegato dalle funzioni è un buon modo per scoprire se ciò che hai fatto ha fatto la differenza, ma per scoprire cosa fare hai bisogno di una tecnica diversa. Questo è ciò che raccomando e so che puoi farlo su iPhone.
Modifica: i revisori mi hanno suggerito di elaborare la risposta, quindi sto cercando di pensare a un modo breve per dirlo.
Il tuo programma generale richiede abbastanza tempo per disturbarti. Supponiamo che siano N secondi.
Stai assumendo che puoi accelerarlo. L'unico modo per farlo è far sì che non faccia qualcosa che sta facendo in quel momento, tenendo conto di m secondi.
Inizialmente non sai cos'è quella cosa. Puoi indovinare, come fanno tutti i programmatori, ma potrebbe facilmente essere qualcos'altro. Qualunque cosa sia, ecco come puoi trovarla:
Dal momento che quella cosa, qualunque essa sia, rappresenta la frazione m / N del tempo, ciò significa che se la metti in pausa a caso la probabilità è m / N che la prenderai nell'atto di fare quella cosa. Naturalmente potrebbe fare qualcos'altro, ma mettilo in pausa e vedi cosa sta facendo.
Ora fallo di nuovo. Se lo vedi fare di nuovo la stessa cosa, puoi essere più sospettoso.
Fallo 10 volte o 20. Ora se lo vedi fare qualcosa di particolare (non importa come lo descrivi) in più pause, di cui puoi liberarti, sai due cose. Sai molto approssimativamente quale frazione di tempo ci vuole, ma sai esattamente cosa riparare.
Se vuoi anche sapere esattamente quanto tempo verrà risparmiato, è facile. Misuralo prima, riparalo e misuralo dopo. Se sei davvero deluso, esci dalla correzione.
Vedi come questo è diverso dalla misurazione? Sta trovando, non misurando . La maggior parte della profilazione si basa sulla misurazione quanto più esattamente possibile del tempo impiegato, come se fosse importante, e fa emergere il problema di identificare ciò che deve essere risolto. La profilazione non trova tutti i problemi, ma questo metodo trova tutti i problemi, ed è i problemi che non trovi che ti feriscono.
Ecco un altro modo, in Swift, di farlo usando la parola chiave differire
func methodName() {
let methodStart = Date()
defer {
let executionTime = Date().timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")
}
// do your stuff here
}
Dai documenti di Apple : un'istruzione di differimento viene utilizzata per eseguire il codice appena prima di trasferire il controllo del programma al di fuori dell'ambito in cui appare l'istruzione di differimento.
Questo è simile a un blocco try / finally con il vantaggio di raggruppare il codice correlato.
Lo uso nella mia libreria 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.")
}
}
... quindi chiama un metodo come:
func myFunctionCall() {
let timer = PrintTimer()
// ...
timer.done()
}
... che a sua volta si presenta così nella console dopo l'esecuzione:
MyFile.swift:225 - myFunctionCall() took 1.8623 s.
Non così conciso come TICK / TOCK sopra, ma è abbastanza chiaro per vedere cosa sta facendo e include automaticamente ciò che viene cronometrato (per file, riga all'inizio del metodo e nome della funzione). Ovviamente se volevo maggiori dettagli (ad esempio, se non sto solo impostando il tempismo di una chiamata al metodo come nel solito caso, ma sto invece calcolando un blocco all'interno di quel metodo) posso aggiungere il parametro "name =" Foo "" sull'iniz di PrintTimer per nominarlo qualcosa oltre alle impostazioni predefinite.
Dal momento che vuoi ottimizzare il tempo che passa da una pagina all'altra in UIWebView, non significa che stai davvero cercando di ottimizzare il Javascript usato nel caricamento di queste pagine?
A tal fine, guarderei un profiler WebKit come quello di cui abbiamo parlato qui:
http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/
Un altro approccio sarebbe quello di iniziare ad alto livello e pensare a come progettare le pagine Web in questione per ridurre al minimo i tempi di caricamento utilizzando il caricamento delle pagine in stile AJAX invece di aggiornare ogni volta l'intera vista web.
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)
}
Ecco una soluzione Swift 3 per bisecare il codice ovunque per trovare un processo di lunga durata.
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()
}
Utilizzo: -
instrument("View Did Appear")
print("ELAPSED TIMES \(elapsedTimes)")
Uscita campione: -
TEMPI ELAPSATI [MyApp.SomeViewController.Instrumentation (titolo: "Start View Did Load", punto: 1, tempo trascorso: 0.040504038333892822), MyApp.SomeViewController.Instrumentation (titolo: "Finished Adding SubViews", point: 2, elapsedTime: 0.01058550117, MyApp.SomeViewController.Instrumentation (titolo: "View Did Appear", punto: 3, tempo trascorso: 0,56564098596572876)]
molte risposte sono strane e non danno risultati in millisecondi (ma in pochi secondi o altro):
ecco cosa uso per ottenere la SM (MILLISECONDI):
Swift:
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 );
Per Swift 4, aggiungi come delegato alla tua classe:
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!))")
}
}
}
Aggiungi alla nostra classe:
class MyViewcontroller: UIViewController, TimingDelegate
Quindi aggiungi alla tua classe:
var _TICK: Date?
Quando vuoi cronometrare qualcosa, inizia con:
TICK
E termina con:
TOCK("Timing the XXX routine")