web-dev-qa-db-fra.com

Comment enregistrer le temps d'exécution d'une méthode exactement en millisecondes?

Existe-t-il un moyen de déterminer le temps d'exécution d'une méthode (en millisecondes)?

203
dan
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 avec une précision inférieure à la milliseconde.

414
Matthew McGoogan

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;
243
Ron

Pour un minutage précis sous 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 fines doivent s’appliquer; il est probablement préférable de faire appel à la routine testée plusieurs fois, et de calculer une moyenne/un minimum/une autre forme de traitement.

De plus, notez que vous pouvez trouver plus utile de profil exécuter votre application en utilisant un outil tel que Shark. Cela ne vous donnera pas les informations de minutage exactes, mais vous indiquera quel pourcentage du temps de l'application est passé où, ce qui est souvent plus utile (mais pas toujours).

48
Stephen Canon

Dans Swift, j'utilise:

Dans ma 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()
  • ce code est basé sur le code de Ron traduit en Swift, il a les crédits
  • J'utilise la date de début au niveau mondial, toute suggestion d'amélioration est la bienvenue 
11
Adriano Spadoni

Je sais que c’est un vieux problème, mais même si je me suis retrouvé à errer devant, j’ai pensé que je soumettrais ma propre option ici.

Le mieux est de consulter mon article de blog à ce sujet: Chronométrer les choses en Objective-C: un chronomètre

Fondamentalement, j’ai écrit un cours qui arrête de regarder de manière très basique mais qui est encapsulé de sorte que vous n’ayez besoin que de 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 post pour un peu plus ou téléchargez-le ici: MMStopwatch.Zip

9
bladnman

J'utilise des macros basées sur Ron's solution.

#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

7
Sergey Teryokhin

J'utilise très peu d'implémentation de classe d'une page inspirée par le code de cet article 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

Son utilisation est très simple: 

  • il suffit d'appeler [DBGStopwatch start:@"slow-operation"]; au début
  • puis [DBGStopwatch stop:@"slow-operation"]; après l'arrivée, pour avoir le temps
4
zubko

Vous pouvez obtenir vraiment un minutage précis (quelques secondes) en utilisant cette classe StopWatch. Il utilise le minuteur haute précision de l'iPhone. En utilisant NSDate, vous n’obtiendrez que la seconde précision. Cette version est conçue spécifiquement pour autorelease et objective-c. J'ai aussi 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

Chronomètre.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 méthode stopWatch statique qui retourne un objet autoreleased. 

Une fois que vous avez appelé start, utilisez la méthode seconds pour obtenir le temps écoulé. Appelez à nouveau start pour le redémarrer. Ou stop pour l'arrêter. Vous pouvez toujours lire l'heure (appelez seconds) à tout moment après avoir appelé stop.

Exemple dans une fonction (appel d'exécution d'exécution)

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

   ... do stuff

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

Un exemple de minutage 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")
}
2
jbg

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;
}
2
cleexiang

OK, si votre objectif est de savoir ce que vous pouvez corriger pour accélérer les choses, c’est un objectif légèrement 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 méthode différente. technique. C'est ce que je recommande , et je sais que vous pouvez le faire sur les iPhones.

Edit: Les réviseurs m'ont suggéré d'élaborer la réponse, alors j'essaie de penser à un moyen bref de le dire.
Votre programme global prend suffisamment de temps d'horloge pour vous déranger. Supposons que ce soit N secondes.
Vous supposez que vous pouvez accélérer les choses. La seule façon de le faire est de ne pas faire ce que vous faites pendant ce temps, en tenant compte de m secondes.
Au départ, vous ne savez pas en quoi consiste cette chose. Vous pouvez deviner, comme le font tous les programmeurs, mais cela pourrait facilement être autre chose. Quoi que ce soit, voici comment vous pouvez le trouver:

Puisque cette chose, quelle qu’elle soit, rend compte de la fraction m/N du temps, cela signifie que si vous la suspendez au hasard, la probabilité est m/N que vous allez attraper dans l'acte de faire cette chose. Bien sûr, il peut s'agir de quelque chose d'autre, mais mettez-le en pause pour voir ce qu'il fait.
Maintenant, recommencez. 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 à peu près combien de temps cela prend, mais vous savez très exactement ce qu’il faut réparer.
Si vous voulez également savoir très exactement combien de temps sera économisé, 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 des profils consistent à mesurer le plus exactement possible le temps pris, comme si cela était important, et à cerner le problème de l'identification des problèmes à résoudre. 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 font mal.

2
Mike Dunlavey

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 laisser tomber.

2
David Kanarek

Il existe un wrapper pratique pour mach_absolute_time() - c’est une fonction CACurrentMediaTime().

Contrairement aux décalages NSDate ou CFAbsoluteTimeGetCurrent(), mach_absolute_time() et CACurrentMediaTime() sont basés sur le horloge interne de l'hôte, une mesure précise, monoatomique, et non soumise à changements dans la référence de temps externe, tels que ceux causés par le temps zones, heure avancée ou 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")
2
Alex Nazaroff

J'utilise ceci dans ma bibliothèque 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 assez 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 simplement un appel de méthode comme dans le cas habituel, mais que je chronomètre un bloc dans cette méthode), je peux ajouter le paramètre "name =" Foo "" sur le PrintTimer init pour l'appeler quelque chose en plus des valeurs par défaut.

0
Tom Dibble

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
}

A partir de docs : d'Apple, une instruction de retrait sert à exécuter du code juste avant le transfert du contrôle de programme en dehors de la portée dans laquelle l'instruction de renvoi apparaît.

Ceci est similaire à un bloc try/finally avec l'avantage de regrouper le code correspondant.

0
CMont