web-dev-qa-db-fra.com

Mesurer le temps écoulé rapidement

Comment pouvons-nous mesurer le temps écoulé pour exécuter une fonction dans Swift? J'essaie d'afficher le temps écoulé comme ceci: "Le temps imparti est de 0,05 seconde". Vu que en Java , nous pouvons utiliser System.nanoTime (), existe-t-il des méthodes équivalentes? Des méthodes sont disponibles dans Swift pour accomplir cela. 

S'il vous plaît jeter un oeil à l'exemple de programme:

func isPrime(var number:Int) ->Bool {
    var i = 0;
    for i=2; i<number; i++ {
        if(number % i == 0 && i != 0) {
            return false;
        }
    }
    return true;
}

var number = 5915587277;

if(isPrime(number)) {
    println("Prime number");
} else {
    println("NOT a prime number");
}
94
Vaquita

Voici une fonction Swift que j'ai écrite pour mesurer Project Euler problèmes dans Swift

Depuis Swift 3, il existe maintenant une version de Grand Central Dispatch qui est "accélérée". La bonne réponse est donc probablement d'utiliser l'API DispatchTime .

Ma fonction ressemblerait à quelque chose comme:

// Swift 3
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
    print("Evaluating problem \(problemNumber)")

    let start = DispatchTime.now() // <<<<<<<<<< Start time
    let myGuess = problemBlock()
    let end = DispatchTime.now()   // <<<<<<<<<<   end time

    let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)

    let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds // <<<<< Difference in nano seconds (UInt64)
    let timeInterval = Double(nanoTime) / 1_000_000_000 // Technically could overflow for long running tests

    print("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
    return theAnswer
}

Ancienne réponse

Pour Swift 1 et 2, ma fonction utilise NSDate:

// Swift 1
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
    println("Evaluating problem \(problemNumber)")

    let start = NSDate() // <<<<<<<<<< Start time
    let myGuess = problemBlock()
    let end = NSDate()   // <<<<<<<<<<   end time

    let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)

    let timeInterval: Double = end.timeIntervalSinceDate(start) // <<<<< Difference in seconds (double)

    println("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
    return theAnswer
}

Notez que l’utilisation de NSdate pour les fonctions de minutage est déconseillée: "L’heure du système peut diminuer en raison de la synchronisation avec des références de temps externes ou du changement explicite de l’horloge par l’utilisateur.".

176
JeremyP

Il s'agit d'une classe de minuteur pratique basée sur CoreFoundations CFAbsoluteTime:

import CoreFoundation

class ParkBenchTimer {

    let startTime:CFAbsoluteTime
    var endTime:CFAbsoluteTime?

    init() {
        startTime = CFAbsoluteTimeGetCurrent()
    }

    func stop() -> CFAbsoluteTime {
        endTime = CFAbsoluteTimeGetCurrent()

        return duration!
    }

    var duration:CFAbsoluteTime? {
        if let endTime = endTime {
            return endTime - startTime
        } else {
            return nil
        }
    }
}

Vous pouvez l'utiliser comme ceci:

let timer = ParkBenchTimer()

// ... a long runnig task ...

println("The task took \(timer.stop()) seconds.")
60
Klaas

Utilisez clock , ProcessInfo.systemUptime ou DispatchTime pour une heure de démarrage simple.


À ma connaissance, il existe au moins dix façons de mesurer le temps écoulé:

Horloge monotone basée:

  1. ProcessInfo.systemUptime .
  2. mach_absolute_time avec mach_timebase_info comme indiqué dans this answer .
  3. clock() in POSIX standard .
  4. times() in POSIX standard . (Trop compliqué car nous avons besoin. De prendre en compte le temps utilisateur par rapport au temps système, et les processus enfants sont impliqués.).
  5. DispatchTime (un wrapper autour de l'API de temps Mach) tel que mentionné par JeremyP dans la réponse acceptée.
  6. CACurrentMediaTime() .

Horloge murale basée:

(n'utilisez jamais ceux-ci pour les métriques: voir ci-dessous pourquoi)

  1. NSDate/Date comme mentionné par d'autres.
  2. CFAbsoluteTime comme mentionné par d'autres.
  3. DispatchWallTime .
  4. gettimeofday() in POSIX standard .

Les options 1, 2 et 3 sont détaillées ci-dessous.

Option 1: API d'informations de processus dans Foundation

do {
    let info = ProcessInfo.processInfo
    let begin = info.systemUptime
    // do something
    let diff = (info.systemUptime - begin)
}

diff:NSTimeInterval est le temps écoulé en secondes.

Option 2: API Mach C

do {
    var info = mach_timebase_info(numer: 0, denom: 0)
    mach_timebase_info(&info)
    let begin = mach_absolute_time()
    // do something
    let diff = Double(mach_absolute_time() - begin) * Double(info.numer) / Double(info.denom)
}

diff:Double est le temps écoulé en nanosecondes.

Option 3: API d'horloge POSIX

do {
    let begin = clock()
    // do something
    let diff = Double(clock() - begin) / Double(CLOCKS_PER_SEC)
}

diff:Double est le temps écoulé en secondes.

Pourquoi pas l'heure de l'horloge murale?

Dans la documentation de CFAbsoluteTimeGetCurrent :

Les appels répétés à cette fonction ne garantissent pas de façon monotone résultats croissants.

La raison est similaire à currentTimeMillis vs nanoTime en Java :

Vous ne pouvez pas utiliser l'un à l'autre fin. La raison en est que non l'horloge de l'ordinateur est parfaite; il dérive toujours et occasionnellement doit être corrigé. Cette correction peut soit arriver manuellement, ou dans le cas de la plupart des machines, il existe un processus qui exécute et émet continuellement de petites corrections à l’horloge système ("horloge murale"). Celles-ci ont tendance à se produire souvent. Une autre correction de ce type se produit chaque fois qu'il y a une seconde intercalaire.

Ici, CFAbsoluteTime fournit l'heure de l'horloge murale au lieu de l'heure de démarrage NSDate est aussi l'heure de l'horloge murale.

37
Franklin Yu
let start = NSDate()

for index in 1...10000 {
    // do nothing
}

let elapsed = start.timeIntervalSinceNow

// elapsed is a negative value.
13
user3370455

Réponse rapide de Swift 4:

let startingPoint = Date()
//  ... intensive task
print("\(startingPoint.timeIntervalSinceNow * -1) seconds elapsed")

Il vous imprimera quelque chose comme 1.02107906341553 secondes écoulées (le temps varie bien sûr en fonction de la tâche, je vous montre simplement ceci pour que vous puissiez voir le niveau de précision décimale de cette mesure).

J'espère que cela aidera quelqu'un dans Swift 4 à partir de maintenant!

11
Mauricio Chirino

Vous pouvez créer une fonction time pour mesurer vos appels . Je suis inspiré par Klaas ' answer.

func time <A> (f: @autoclosure () -> A) -> (result:A, duration: String) {
    let startTime = CFAbsoluteTimeGetCurrent()
    let result = f()
    let endTime = CFAbsoluteTimeGetCurrent()
    return (result, "Elapsed time is \(endTime - startTime) seconds.")
}

Cette fonction vous permettrait de l'appeler comme ceci time (isPrime(7)), ce qui renverrait un tuple contenant le résultat et une description de la chaîne du temps écoulé.

Si vous ne souhaitez que le temps écoulé, vous pouvez le faire time (isPrime(7)).duration

6
Mellson

Il suffit de copier et coller cette fonction. Écrit dans Swift 5. Copier JeremyP ici.

func calculateTime(block : (() -> Void)) {
        let start = DispatchTime.now()
        block()
        let end = DispatchTime.now()
        let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
        let timeInterval = Double(nanoTime) / 1_000_000_000
        print("Time: \(timeInterval) seconds")
    }

Utilisez-le comme

calculateTime {
     exampleFunc()// function whose execution time to be calculated
}
2
ShaileshAher

Voici mon essai pour la réponse la plus simple:

let startTime = Date().timeIntervalSince1970  // 1512538946.5705 seconds

// time passes (about 10 seconds)

let endTime = Date().timeIntervalSince1970    // 1512538956.57195 seconds
let elapsedTime = endTime - startTime         // 10.0014500617981 seconds

Remarques

  • startTime et endTime sont du type TimeInterval , qui est simplement un typealias pour Double, il est donc facile de le convertir en Int ou autre Le temps est mesuré en secondes avec une précision inférieure à la milliseconde.
  • Voir aussi DateInterval , qui inclut une heure de début et de fin réelle.
  • L'utilisation de l'heure depuis 1970 est similaire à horodatages Java .
2
Suragch

Fonction d'assistance simple pour mesurer le temps d'exécution avec fermeture.

func printExecutionTime(withTag tag: String, of closure: () -> ()) {
    let start = CACurrentMediaTime()
    closure()
    print("#\(tag) - execution took \(CACurrentMediaTime() - start) seconds")
}

Utilisation:

printExecutionTime(withTag: "Init") {
    // Do your work here
}

Résultat: #Init - execution took 1.00104497105349 seconds

2
Vadim Akhmerov

J'utilise ceci:

public class Stopwatch {
    public init() { }
    private var start_: NSTimeInterval = 0.0;
    private var end_: NSTimeInterval = 0.0;

    public func start() {
        start_ = NSDate().timeIntervalSince1970;
    }

    public func stop() {
        end_ = NSDate().timeIntervalSince1970;
    }

    public func durationSeconds() -> NSTimeInterval {
        return end_ - start_;
    }
}

Je ne sais pas si c'est plus ou moins précis que précédemment. Mais les secondes ont beaucoup de décimales et semblent intercepter de petits changements de code dans des algorithmes comme QuickSort en utilisant swap () par rapport à swap urself etc.

Pensez à augmenter vos optimisations de construction lorsque vous testez les performances:

Swift compiler optimizations

2
Peheje

vous pouvez mesurer les nanosecondes comme par exemple. ce:

let startDate: NSDate = NSDate()

// your long procedure

let endDate: NSDate = NSDate()
let dateComponents: NSDateComponents = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian).components(NSCalendarUnit.CalendarUnitNanosecond, fromDate: startDate, toDate: endDate, options: NSCalendarOptions(0))
println("runtime is nanosecs : \(dateComponents.nanosecond)")
2
holex

Enveloppez-le dans un bloc d'achèvement pour une utilisation facile.

public class func secElapsed(completion: () -> Void) {
    let startDate: NSDate = NSDate()
    completion()
    let endDate: NSDate = NSDate()
    let timeInterval: Double = endDate.timeIntervalSinceDate(startDate)
    println("seconds: \(timeInterval)")
}
1
RiceAndBytes

J'ai emprunté l'idée de Klaas pour créer une structure légère pour mesurer le temps d'exécution et d'intervalle:

Utilisation du code:

var timer = RunningTimer.init()
// Code to be timed
print("Running: \(timer) ") // Gives time interval
// Second code to be timed
print("Running: \(timer) ") // Gives final time

Il n'est pas nécessaire d'appeler la fonction d'arrêt, car la fonction d'impression donnera le temps écoulé. Il peut être appelé à plusieurs reprises pour obtenir le temps écoulé. Mais pour arrêter le chronomètre à un certain point du code, utilisez timer.stop(), il peut également être utilisé pour renvoyer le temps en secondes: let seconds = timer.stop() Une fois le chronomètre arrêté, le minuteur d'intervalle ne sera pas, de sorte que print("Running: \(timer) ") donnera la valeur correcte. même après quelques lignes de code.

Voici le code pour RunningTimer. Il est testé pour Swift 2.1:

import CoreFoundation
// Usage:    var timer = RunningTimer.init()
// Start:    timer.start() to restart the timer
// Stop:     timer.stop() returns the time and stops the timer
// Duration: timer.duration returns the time
// May also be used with print(" \(timer) ")

struct RunningTimer: CustomStringConvertible {
    var begin:CFAbsoluteTime
    var end:CFAbsoluteTime

    init() {
        begin = CFAbsoluteTimeGetCurrent()
        end = 0
    }
    mutating func start() {
        begin = CFAbsoluteTimeGetCurrent()
        end = 0
    }
    mutating func stop() -> Double {
        if (end == 0) { end = CFAbsoluteTimeGetCurrent() }
        return Double(end - begin)
    }
    var duration:CFAbsoluteTime {
        get {
            if (end == 0) { return CFAbsoluteTimeGetCurrent() - begin } 
            else { return end - begin }
        }
    }
    var description:String {
    let time = duration
    if (time > 100) {return " \(time/60) min"}
    else if (time < 1e-6) {return " \(time*1e9) ns"}
    else if (time < 1e-3) {return " \(time*1e6) µs"}
    else if (time < 1) {return " \(time*1000) ms"}
    else {return " \(time) s"}
    }
}
1
Hannes Sverrisson

C’est l’extrait que j’ai conçu et qui semble fonctionner pour moi sur mon Macbook avec Swift 4.

Jamais testé sur d'autres systèmes, mais j'ai pensé qu'il valait la peine de le partager.

typealias MonotonicTS = UInt64
let monotonic_now: () -> MonotonicTS = mach_absolute_time

let time_numer: UInt64
let time_denom: UInt64
do {
    var time_info = mach_timebase_info(numer: 0, denom: 0)
    mach_timebase_info(&time_info)
    time_numer = UInt64(time_info.numer)
    time_denom = UInt64(time_info.denom)
}

// returns time interval in seconds
func monotonic_diff(from: MonotonicTS, to: MonotonicTS) -> TimeInterval {
    let diff = (to - from)
    let nanos = Double(diff * time_numer / time_denom)
    return nanos / 1_000_000_000
}

func seconds_elapsed(since: MonotonicTS) -> TimeInterval {
    return monotonic_diff(from: since, to:monotonic_now())
}

Voici un exemple d'utilisation:

let t1 = monotonic_now()
// .. some code to run ..
let elapsed = seconds_elapsed(since: t1)
print("Time elapsed: \(elapsed*1000)ms")

Une autre façon est de le faire plus explicitement:

let t1 = monotonic_now()
// .. some code to run ..
let t2 = monotonic_now()
let elapsed = monotonic_diff(from: t1, to: t2)
print("Time elapsed: \(elapsed*1000)ms")
0
hasen

Basé sur Franklin Yu answer and Cœur commentaires

Détails

  • Xcode 10.1 (10B61)
  • Swift 4.2

Solution 1

mesure (_ :)

Solution 2

import Foundation

class Measurer<T: Numeric> {

    private let startClosure: ()->(T)
    private let endClosure: (_ beginningTime: T)->(T)

    init (startClosure: @escaping ()->(T), endClosure: @escaping (_ beginningTime: T)->(T)) {
        self.startClosure = startClosure
        self.endClosure = endClosure
    }

    init (getCurrentTimeClosure: @escaping ()->(T)) {
        startClosure = getCurrentTimeClosure
        endClosure = { beginningTime in
            return getCurrentTimeClosure() - beginningTime
        }
    }

    func measure(closure: ()->()) -> T {
        let value = startClosure()
        closure()
        return endClosure(value)
    }
}

Utilisation de la solution 2

// Sample with ProcessInfo class

m = Measurer { ProcessInfo.processInfo.systemUptime }
time = m.measure {
    _ = (1...1000).map{_ in Int(arc4random()%100)}
}
print("ProcessInfo: \(time)")

// Sample with Posix clock API

m = Measurer(startClosure: {Double(clock())}) { (Double(clock()) - $0 ) / Double(CLOCKS_PER_SEC) }
time = m.measure {
    _ = (1...1000).map{_ in Int(arc4random()%100)}
}
print("POSIX: \(time)")
0
Vasily Bodnarchuk

Voici comment je l'ai écrit. 

 func measure<T>(task: () -> T) -> Double {
        let startTime = CFAbsoluteTimeGetCurrent()
        task()
        let endTime = CFAbsoluteTimeGetCurrent()
        let result = endTime - startTime
        return result
    }

Pour mesurer un algorithme utilisez il comme ça. 

let time = measure {
    var array = [2,4,5,2,5,7,3,123,213,12]
    array.sorted()
}

print("Block is running \(time) seconds.")
0
BilalReffas

Classe statique Swift3 pour la synchronisation des fonctions de base. Il gardera trace de chaque minuterie par nom. Appelez comme cela au moment où vous voulez commencer à mesurer:

Stopwatch.start(name: "PhotoCapture")

Appelez ceci pour capturer et imprimer le temps écoulé:

Stopwatch.timeElapsed(name: "PhotoCapture")

Voici la sortie: *** PhotoCapture écoulé ms: 1402.415125 Il existe un paramètre "useNanos" si vous souhaitez utiliser nanos . N'hésitez pas à le modifier si nécessaire.

   class Stopwatch: NSObject {

  private static var watches = [String:TimeInterval]()

  private static func intervalFromMachTime(time: TimeInterval, useNanos: Bool) -> TimeInterval {
     var info = mach_timebase_info()
     guard mach_timebase_info(&info) == KERN_SUCCESS else { return -1 }
     let currentTime = mach_absolute_time()
     let nanos = currentTime * UInt64(info.numer) / UInt64(info.denom)
     if useNanos {
        return (TimeInterval(nanos) - time)
     }
     else {
        return (TimeInterval(nanos) - time) / TimeInterval(NSEC_PER_MSEC)
     }
  }

  static func start(name: String) {
     var info = mach_timebase_info()
     guard mach_timebase_info(&info) == KERN_SUCCESS else { return }
     let currentTime = mach_absolute_time()
     let nanos = currentTime * UInt64(info.numer) / UInt64(info.denom)
     watches[name] = TimeInterval(nanos)
  }

  static func timeElapsed(name: String) {
     return timeElapsed(name: name, useNanos: false)
  }

  static func timeElapsed(name: String, useNanos: Bool) {
     if let start = watches[name] {
        let unit = useNanos ? "nanos" : "ms"
        print("*** \(name) elapsed \(unit): \(intervalFromMachTime(time: start, useNanos: useNanos))")
     }
  }

}

0
Gjchoza