¿Hay alguna manera de determinar cuánto tiempo necesita ejecutar un método (en milisegundos)?
¿Hay alguna manera de determinar cuánto tiempo necesita ejecutar un método (en milisegundos)?
Respuestas:
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 y tiene una precisión inferior a milisegundos.
NSLog(@"executionTime = %f", executionTime);
NSDate
y mach_absolute_time()
en alrededor de 30 ms de nivel. 27 vs. 29, 36 vs. 39, 43 vs. 45. NSDate
fue más fácil de usar para mí y los resultados fueron lo suficientemente similares como para no molestarme mach_absolute_time()
.
Aquí hay dos macros de una línea que uso:
#define TICK NSDate *startTime = [NSDate date]
#define TOCK NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])
Úselo así:
TICK;
/* ... Do Some Work Here ... */
TOCK;
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])
hace que esta respuesta también devuelva en qué función se usó el temporizador. Encontré esto útil si utilicé TICK TOCK para cronometrar múltiples funciones.
__PRETTY_FUNCTION__
y __LINE__
si desea información más detallada.
Para una sincronización precisa en OS X, debe usar mach_absolute_time( )
declarado en <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;
Por supuesto, se aplican las advertencias habituales sobre las mediciones de grano fino; Probablemente sea mejor invocar la rutina bajo prueba muchas veces y promediar / tomar un mínimo / alguna otra forma de procesamiento.
Además, tenga en cuenta que puede resultarle más útil perfilar su aplicación que se ejecuta con una herramienta como Shark. Esto no le dará información de tiempo exacta, pero le dirá qué porcentaje del tiempo de la aplicación se está gastando dónde, que a menudo es más útil (pero no siempre).
Hay un contenedor conveniente para mach_absolute_time()
- es una CACurrentMediaTime()
función.
A diferencia
NSDate
oCFAbsoluteTimeGetCurrent()
compensaciones,mach_absolute_time()
yCACurrentMediaTime()
están basados en el reloj interno de acogida, a, medida monoatómico preciso, y no está sujeto a cambios en la referencia de tiempo externa, tales como las causadas por las zonas de tiempo, los cambios de horario, o segundos bisiestos.
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
.
En Swift, estoy usando:
En mi Macros.swift acabo de agregar
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)")
}
ahora puedes llamar a cualquier parte
TICK()
// your code to be tracked
TOCK()
\(-startTime.timeIntervalSinceNow)
(nótese la negativa)
Sé que este es viejo, pero incluso me encontré deambulando de nuevo, así que pensé en presentar mi propia opción aquí.
La mejor apuesta es revisar mi publicación de blog sobre esto: Cronometraje de cosas en Objective-C: Un cronómetro
Básicamente, escribí una clase que deja de mirar de una manera muy básica pero está encapsulada de modo que solo necesita hacer lo siguiente:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
Y terminas con:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
en el registro ...
Nuevamente, mira mi publicación para un poco más o descárgala aquí: MMStopwatch.zip
Yo uso macros basadas en la solución de Ron .
#define TICK(XXX) NSDate *XXX = [NSDate date]
#define TOCK(XXX) NSLog(@"%s: %f", #XXX, -[XXX timeIntervalSinceNow])
Para líneas de código:
TICK(TIME1);
/// do job here
TOCK(TIME1);
veremos en la consola algo como: TIME1: 0.096618
Utilizo una implementación mínima de clase de una página inspirada en el código de esta publicación de 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
Su uso es muy simple:
[DBGStopwatch start:@"slow-operation"];
al principio[DBGStopwatch stop:@"slow-operation"];
después del final, para tener el tiempoPuede obtener una sincronización realmente buena (segundos, partes de segundos) utilizando esta clase StopWatch. Utiliza el temporizador de alta precisión en el iPhone. El uso de NSDate solo le brindará una segunda (s) precisión (s). Esta versión está diseñada específicamente para la liberación automática y el objetivo-c. Tengo una versión de C ++ también si es necesario. Puede encontrar la versión de c ++ aquí .
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 clase tiene una estática. stopWatch
método que devuelve un objeto lanzado automáticamente.
Una vez que llame start
, use el seconds
método para obtener el tiempo transcurrido. Llame start
nuevamente para reiniciarlo. O stop
para detenerlo. Aún puede leer la hora (llamada seconds
) en cualquier momento después de llamar stop
.
Ejemplo en una función (llamada de tiempo de ejecución)
-(void)SomeFunc
{
StopWatch* stopWatch = [StopWatch stopWatch];
[stopWatch Start];
... do stuff
[stopWatch StopWithContext:[NSString stringWithFormat:@"Created %d Records",[records count]]];
}
Yo 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;
}
Yo uso esto:
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);
Pero no estoy seguro acerca de CLOCKS_PER_SEC en el iPhone. Es posible que desee dejarlo.
Un ejemplo de sincronización precisa utilizando 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")
}
OK, si su objetivo es averiguar qué puede arreglar para hacerlo más rápido, ese es un objetivo un poco diferente. Medir el tiempo que tardan las funciones es una buena manera de averiguar si lo que hiciste marcó la diferencia, pero para saber qué hacer necesitas una técnica diferente. Esto es lo que recomiendo , y sé que puedes hacerlo en iPhones.
Editar: los revisores me sugirieron que elaborara la respuesta, así que estoy tratando de pensar en una breve forma de decirla.
Su programa general toma suficiente tiempo de reloj para molestarlo. Supongamos que son N segundos.
Estás asumiendo que puedes acelerarlo. La única forma de hacerlo es haciendo que no haga algo que está haciendo en ese tiempo, representando m segundos.
Inicialmente no sabes qué es esa cosa. Puedes adivinar, como lo hacen todos los programadores, pero fácilmente podría ser otra cosa. Sea lo que sea, así es como puedes encontrarlo:
Dado que esa cosa, sea lo que sea, representa la fracción m / N del tiempo, eso significa que si la pausas al azar, la probabilidad es m / N de que la atraparás en el acto de hacer esa cosa. Por supuesto, podría estar haciendo otra cosa, pero pause y vea qué está haciendo.
Ahora hazlo de nuevo. Si lo ves haciendo lo mismo otra vez, puedes sospechar más.
Hazlo 10 veces, o 20. Ahora, si ves que hace algo en particular (no importa cómo lo describas) en múltiples pausas, de las que puedes deshacerte, sabes dos cosas. Sabes más o menos qué fracción de tiempo lleva, pero sabes exactamente qué arreglar.
Si también desea saber exactamente cuánto tiempo se ahorrará, eso es fácil. Mídelo antes, arréglalo y mídelo después. Si realmente estás decepcionado, retira la solución.
¿Ves cómo esto es diferente de medir? Es encontrar, no medir . La mayoría de los perfiles se basan en medir de la manera más exacta posible cuánto tiempo se toma, como si eso fuera importante, y agita con la mano el problema de identificar lo que necesita ser arreglado. La creación de perfiles no encuentra todos los problemas, pero este método sí encuentra todos los problemas, y son los problemas que no encuentra los que lo lastiman.
Aquí hay otra forma, en Swift, de hacerlo usando la palabra clave de aplazamiento
func methodName() {
let methodStart = Date()
defer {
let executionTime = Date().timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")
}
// do your stuff here
}
De los documentos de Apple : se usa una declaración de aplazamiento para ejecutar código justo antes de transferir el control del programa fuera del alcance en el que aparece la declaración de aplazamiento.
Esto es similar a un bloque try / finally con la ventaja de tener el código relacionado agrupado.
Lo uso en mi biblioteca de utilidades ( 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.")
}
}
... luego llame a un método como:
func myFunctionCall() {
let timer = PrintTimer()
// ...
timer.done()
}
... que a su vez se ve así en la consola después de ejecutar:
MyFile.swift:225 - myFunctionCall() took 1.8623 s.
No es tan conciso como TICK / TOCK anterior, pero es lo suficientemente claro como para ver lo que está haciendo e incluye automáticamente lo que se está cronometrando (por archivo, línea al inicio del método y nombre de la función). Obviamente, si quisiera más detalles (por ejemplo, si no solo estoy cronometrando una llamada al método como es el caso habitual, sino que estoy cronometrando un bloque dentro de ese método), puedo agregar el parámetro "name =" Foo "" en el init PrintTimer por nombrarlo además de los valores predeterminados.
Dado que desea optimizar el tiempo de pasar de una página a otra en un UIWebView, ¿no significa que realmente está buscando optimizar el Javascript utilizado para cargar estas páginas?
Con ese fin, miraría un perfilador de WebKit como el que se menciona aquí:
http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/
Otro enfoque sería comenzar a un alto nivel y pensar cómo puede diseñar las páginas web en cuestión para minimizar los tiempos de carga utilizando la carga de páginas de estilo AJAX en lugar de actualizar toda la vista web 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)
}
Aquí hay una solución Swift 3 para dividir el código en cualquier lugar para encontrar un proceso de larga ejecución.
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)")
Salida de muestra: -
TIEMPOS TRANSCURRIDOS [MyApp.SomeViewController.Instrumentation (título: "Start View Did Load", punto: 1, elapsedTime: 0.040504038333892822), MyApp.SomeViewController.Instrumentation (título: "Finished Adding SubViews", punto: 2, elapsedTime: 0.0105850100517, MyApp.SomeViewController.Instrumentation (título: "La vista apareció", punto: 3, tiempo transcurrido: 0.56564098596572876)]
muchas respuestas son extrañas y realmente no dan resultado en milisegundos (pero en segundos o cualquier otra cosa):
Aquí lo que uso para obtener MS (MILLISECONDS):
Rápido:
let startTime = NSDate().timeIntervalSince1970 * 1000
// your Swift code
let endTimeMinusStartTime = NSDate().timeIntervalSince1970 * 1000 - startTime
print("time code execution \(endTimeMinStartTime) ms")
C objetivo:
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 Swift 4, agregue como delegado a su clase:
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!))")
}
}
}
Añadir a nuestra clase:
class MyViewcontroller: UIViewController, TimingDelegate
Luego agregue a su clase:
var _TICK: Date?
Cuando quieras cronometrar algo, comienza con:
TICK
Y termina con:
TOCK("Timing the XXX routine")