Existe-t-il un moyen de déterminer le temps nécessaire à une méthode pour s'exécuter (en millisecondes)?
Existe-t-il un moyen de déterminer le temps nécessaire à une méthode pour s'exécuter (en millisecondes)?
Réponses:
NSDate *methodStart = [NSDate date];
/* ... Do whatever you need to do ... */
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);
Rapide:
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 à utiliser et d'une précision inférieure à la milliseconde.
NSLog(@"executionTime = %f", executionTime);
NSDate
et mach_absolute_time()
à environ 30 ms. 27 contre 29, 36 contre 39, 43 contre 45. NSDate
était plus facile à utiliser pour moi et les résultats étaient suffisamment similaires pour ne pas déranger mach_absolute_time()
.
Voici deux macros d'une ligne que j'utilise:
#define TICK NSDate *startTime = [NSDate date]
#define TOCK NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])
Utilisez-le comme ceci:
TICK;
/* ... Do Some Work Here ... */
TOCK;
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])
fait que cette réponse retourne également dans quelle fonction la minuterie a été utilisée. J'ai trouvé cela utile si j'utilisais le TICK TOCK pour chronométrer plusieurs fonctions.
__PRETTY_FUNCTION__
et __LINE__
si vous souhaitez des informations plus détaillées.
Pour un timing précis sur OS X, vous devez utiliser mach_absolute_time( )
déclaré dans <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;
Bien sûr, les mises en garde habituelles concernant les mesures à grain fin s'appliquent; il vaut probablement mieux invoquer la routine testée plusieurs fois et faire la moyenne / prendre un minimum / une autre forme de traitement.
De plus, veuillez noter qu'il peut être plus utile de profiler votre application en cours d'exécution à l'aide d'un outil comme Shark. Cela ne vous donnera pas d'informations précises sur le calendrier, mais cela vous indiquera quel pourcentage du temps de l'application est dépensé où, ce qui est souvent plus utile (mais pas toujours).
Il y a un emballage pratique pour mach_absolute_time()
- c'est une CACurrentMediaTime()
fonction.
Contrairement aux décalages
NSDate
ouCFAbsoluteTimeGetCurrent()
décalages,mach_absolute_time()
etCACurrentMediaTime()
sont basés sur l'horloge interne de l'hôte, une mesure précise et anatomique, et ne sont pas soumis à des changements dans la référence temporelle externe, tels que ceux causés par les fuseaux horaires, l'heure d'été ou les secondes intercalaires.
ObjC
CFTimeInterval startTime = CACurrentMediaTime();
// Do your stuff here
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(@"Total Runtime: %g s", endTime - startTime);
Rapide
let startTime = CACurrentMediaTime()
// Do your stuff here
let endTime = CACurrentMediaTime()
print("Total Runtime: \(endTime - startTime) s")
NSDate
.
Dans Swift, j'utilise:
Dans mon Macros.swift, je viens d'ajouter
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)")
}
vous pouvez maintenant appeler n'importe où
TICK()
// your code to be tracked
TOCK()
\(-startTime.timeIntervalSinceNow)
(remarquez le négatif)
Je sais que c'est un ancien mais même je me suis retrouvé à le repasser, alors j'ai pensé soumettre ma propre option ici.
Le meilleur pari est de consulter mon article de blog à ce sujet: chronométrer les choses dans Objective-C: un chronomètre
Fondamentalement, j'ai écrit une classe qui arrête de regarder de manière très basique mais est encapsulée de sorte que vous n'avez qu'à faire ce qui suit:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
Et vous vous retrouvez avec:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
dans le journal ...
Encore une fois, consultez mon article pour un peu plus ou téléchargez-le ici: MMStopwatch.zip
J'utilise des macros basées sur la solution de Ron .
#define TICK(XXX) NSDate *XXX = [NSDate date]
#define TOCK(XXX) NSLog(@"%s: %f", #XXX, -[XXX timeIntervalSinceNow])
Pour les lignes de code:
TICK(TIME1);
/// do job here
TOCK(TIME1);
nous verrons dans la console quelque chose comme: TIME1: 0.096618
J'utilise une implémentation de classe d'une page très minimale inspirée du code de ce 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
Son utilisation est très simple:
[DBGStopwatch start:@"slow-operation"];
au début[DBGStopwatch stop:@"slow-operation"];
après l'arrivée, pour avoir le tempsVous pouvez obtenir un timing très fin (secondes, parties de secondes) en utilisant cette classe StopWatch. Il utilise la minuterie haute précision de l'iPhone. L'utilisation de NSDate n'obtiendra qu'une précision d'une seconde. Cette version est conçue spécifiquement pour autorelease et objective-c. J'ai également une version c ++ si nécessaire. Vous pouvez trouver la version c ++ ici .
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 a une stopWatch
méthode statique qui renvoie un objet libéré automatiquement.
Une fois que vous appelez start
, utilisez la seconds
méthode pour obtenir le temps écoulé. Appelez à start
nouveau pour le redémarrer. Ou stop
pour l'arrêter. Vous pouvez toujours lire l'heure (appel seconds
) à tout moment après l'appel stop
.
Exemple dans une fonction (temporisation de l'exécution)
-(void)SomeFunc
{
StopWatch* stopWatch = [StopWatch stopWatch];
[stopWatch Start];
... do stuff
[stopWatch StopWithContext:[NSString stringWithFormat:@"Created %d Records",[records count]]];
}
J'utilise ce code:
#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;
}
J'utilise ceci:
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);
Mais je ne suis pas sûr de CLOCKS_PER_SEC sur l'iPhone. Vous voudrez peut-être le laisser de côté.
Un exemple de synchronisation à grain fin utilisant mach_absolute_time()
dans 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 votre objectif est de trouver ce que vous pouvez corriger pour le rendre plus rapide, c'est un objectif un peu différent. Mesurer le temps que prennent les fonctions est un bon moyen de savoir si ce que vous avez fait a fait une différence, mais pour savoir quoi faire, vous avez besoin d'une technique différente. C'est ce que je recommande , et je sais que vous pouvez le faire sur les iPhones.
Edit: Les critiques ont suggéré que j'élabore la réponse, donc j'essaie de penser à une brève façon de le dire.
Votre programme global prend suffisamment de temps pour vous déranger. Supposons que ce soit N secondes.
Vous supposez que vous pouvez l'accélérer. La seule façon de le faire est de ne pas faire ce qu'il fait pendant ce temps, ce qui représente m secondes.
Vous ne savez pas au départ ce que c'est. Vous pouvez deviner, comme le font tous les programmeurs, mais cela pourrait facilement être autre chose. Quoi que ce soit, voici comment le trouver:
Puisque cette chose, quelle qu'elle soit, représente la fraction m / N du temps, cela signifie que si vous la mettez en pause au hasard, la probabilité est m / N que vous l'attrapiez en train de faire cette chose. Bien sûr, il pourrait faire autre chose, mais faites une pause et voyez ce qu'il fait.
Maintenant recommence. Si vous le voyez refaire la même chose, vous pouvez être plus méfiant.
Faites-le 10 fois, ou 20. Maintenant, si vous le voyez faire quelque chose de particulier (peu importe comment vous le décrivez) sur plusieurs pauses, dont vous pouvez vous débarrasser, vous savez deux choses. Vous savez très approximativement quelle fraction de temps cela prend, mais vous savez très exactement quoi réparer.
Si vous voulez également savoir très exactement combien de temps vous économiserez, rien de plus simple. Mesurez-le avant, réparez-le et mesurez-le après. Si vous êtes vraiment déçu, annulez le correctif.
Voyez-vous en quoi cela diffère de la mesure? C'est trouver, pas mesurer . La plupart du profilage est basé sur la mesure aussi exacte que possible du temps nécessaire, comme si c'était important, et remet en question le problème de l'identification de ce qui doit être corrigé. Le profilage ne trouve pas tous les problèmes, mais cette méthode trouve tous les problèmes, et ce sont les problèmes que vous ne trouvez pas qui vous blessent.
Voici une autre façon, dans Swift, de le faire en utilisant le mot clé defer
func methodName() {
let methodStart = Date()
defer {
let executionTime = Date().timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")
}
// do your stuff here
}
À partir des documents d'Apple : une instruction defer est utilisée pour exécuter du code juste avant de transférer le contrôle du programme en dehors de la portée dans laquelle l'instruction defer apparaît.
Ceci est similaire à un bloc try / finally avec l'avantage de regrouper le code associé.
J'utilise ceci dans ma bibliothèque d'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.")
}
}
... puis appelez une méthode comme:
func myFunctionCall() {
let timer = PrintTimer()
// ...
timer.done()
}
... qui à son tour ressemble à ceci dans la console après avoir exécuté:
MyFile.swift:225 - myFunctionCall() took 1.8623 s.
Pas aussi concis que TICK / TOCK ci-dessus, mais il est suffisamment clair pour voir ce qu'il fait et inclut automatiquement ce qui est chronométré (par fichier, ligne au début de la méthode et nom de la fonction). Évidemment, si je voulais plus de détails (par exemple, si je ne chronomètre pas seulement un appel de méthode comme c'est le cas d'habitude, mais plutôt que je chronomètre un bloc dans cette méthode), je peux ajouter le paramètre "name =" Foo "" sur l'init PrintTimer pour le nommer quelque chose en plus des valeurs par défaut.
Puisque vous souhaitez optimiser le temps de passage d'une page à l'autre dans une UIWebView, cela ne signifie-t-il pas que vous cherchez vraiment à optimiser le Javascript utilisé pour charger ces pages?
À cette fin, je regarderais un profileur WebKit comme celui dont nous avons parlé ici:
http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/
Une autre approche consisterait à commencer à un niveau élevé et à réfléchir à la manière de concevoir les pages Web en question afin de minimiser les temps de chargement à l'aide du chargement des pages de style AJAX au lieu de rafraîchir la vue Web entière à chaque fois.
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)
}
Voici une solution Swift 3 pour bissecter du code n'importe où pour trouver un long processus.
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()
}
Utilisation: -
instrument("View Did Appear")
print("ELAPSED TIMES \(elapsedTimes)")
Exemple de sortie: -
TEMPS ÉCOULÉ [MyApp.SomeViewController.Instrumentation (titre: "Start View Did Load", point: 1, elapsedTime: 0.040504038333892822), MyApp.SomeViewController.Instrumentation (titre: "Finished Adding SubViews", point: 2, elapsedTime: 0.01058501005172729 MyApp.SomeViewController.Instrumentation (titre: "View Did Appear", point: 3, elapsedTime: 0.56564098596572876)]
de nombreuses réponses sont étranges et ne donnent pas vraiment de résultat en millisecondes (mais en quelques secondes ou autre):
voici ce que j'utilise pour obtenir MS (MILLISECONDS):
Rapide:
let startTime = NSDate().timeIntervalSince1970 * 1000
// your Swift code
let endTimeMinusStartTime = NSDate().timeIntervalSince1970 * 1000 - startTime
print("time code execution \(endTimeMinStartTime) ms")
Objectif 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 );
Pour Swift 4, ajoutez en tant que délégué à votre 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!))")
}
}
}
Ajoutez à notre classe:
class MyViewcontroller: UIViewController, TimingDelegate
Ajoutez ensuite à votre classe:
var _TICK: Date?
Lorsque vous voulez chronométrer quelque chose, commencez par:
TICK
Et terminez par:
TOCK("Timing the XXX routine")