Mida el tiempo transcurrido en forma rápida

¿Cómo podemos medir el tiempo transcurrido para ejecutar una función en Swift? Estoy tratando de mostrar el tiempo transcurrido de esta manera: “El tiempo Elasped es .05 segundos”. Vimos que en Java , podemos usar System.nanoTime (), ¿hay métodos de métodos equivalentes disponibles en Swift para lograr esto?

Por favor, eche un vistazo al progtwig de ejemplo:

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"); } 

Aquí hay una función Swift que escribí para medir problemas de Project Euler en Swift

Utiliza 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 } 

Actualizar

A partir de Swift 3, ahora hay una versión de Grand Central Dispatch que es "swifitfied". Entonces, la nueva respuesta correcta probablemente sea usar la API DispatchTime .

Mi función se vería así (no probada ni nada):

 // 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 } 

Esta es una práctica clase de temporizador basada en CoreFoundation 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 } } } 

Puedes usarlo así:

 let timer = ParkBenchTimer() // ... a long runnig task ... println("The task took \(timer.stop()) seconds.") 

Use clock , ProcessInfo.systemUptime o DispatchTime para un tiempo de inicio simple.


Hay, por lo que sé, al menos siete formas de medir el tiempo transcurrido:

  1. NSDate como lo mencionaron otros.
  2. CFAbsoluteTime como lo mencionaron otros.
  3. ProcessInfo.systemUptime .
  4. mach_absolute_time con mach_timebase_info como se menciona en esta respuesta .
  5. clock() en el estándar POSIX .
  6. times() en el estándar POSIX . (Demasiado complicado ya que tenemos que considerar el tiempo del usuario frente al tiempo del sistema, y ​​los procesos secundarios están involucrados).
  7. DispatchTime (un contenedor alrededor de Mach time API) como lo menciona JeremyP en la respuesta aceptada.

Las opciones 3, 4 y 5 se detallan a continuación.

Opción 3: API de información de proceso en Foundation

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

donde diff:NSTimeInterval es el tiempo transcurrido por segundos.

Opción 4: API de 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) } 

donde diff:Double es el tiempo transcurrido por nano segundos.

Opción 5: API de reloj POSIX

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

donde diff:Double es el tiempo transcurrido por segundos.

¿Por qué no la hora del reloj de pared para el tiempo transcurrido?

En la documentación de CFAbsoluteTimeGetCurrent :

Las llamadas repetidas a esta función no garantizan resultados monótonamente crecientes.

La razón es similar a la nanoTime vs nanoTime en Java :

No puedes usar el uno para el otro propósito. La razón es que el reloj de ninguna computadora es perfecto; siempre se desplaza y de vez en cuando necesita ser corregido. Esta corrección puede ocurrir manualmente o, en el caso de la mayoría de las máquinas, hay un proceso que se ejecuta y continuamente emite pequeñas correcciones al reloj del sistema (“reloj de pared”). Estos tienden a suceder a menudo. Otra corrección de este tipo ocurre cada vez que hay un segundo intercalar.

Aquí, CFAbsoluteTime proporciona el tiempo del reloj de pared en lugar del tiempo de inicio. NSDate es probablemente el reloj de pared también.

 let start = NSDate() for index in 1...10000 { // do nothing } let elapsed = start.timeIntervalSinceNow // elapsed is a negative value. 

Swift 4 respuesta más corta:

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

Te imprimirá algo así como 1.02107906341553 segundos transcurridos (el tiempo por supuesto variará dependiendo de la tarea, solo estoy mostrando esto para que ustedes vean el nivel de precisión decimal para esta medición).

Espero que ayude a alguien en Swift 4 a partir de ahora!

Puede crear una función de time para medir sus llamadas. Estoy inspirado por la respuesta de Klaas .

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

Esta función le permitirá llamarlo como esta time (isPrime(7)) que devolverá una tupla que contiene el resultado y una descripción de cadena del tiempo transcurrido.

Si solo desea el tiempo transcurrido, puede hacerlo esta time (isPrime(7)).duration

puedes medir nanosegundos como por ejemplo esto:

 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)") 

Yo uso esto:

 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_; } } 

No sé si es más o menos exacto que lo publicado anteriormente. Pero los segundos tienen una gran cantidad de decimales y parecen detectar pequeños cambios de código en algoritmos como QuickSort usando swap () frente a implementar swap urself, etc.

Recuerde boost sus optimizaciones de comstackción cuando pruebe el rendimiento:

Optimizaciones del compilador Swift

Función de ayuda simple para medir el tiempo de ejecución con cierre.

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

Uso:

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

Resultado: #Init - execution took 1.00104497105349 seconds

Tomé prestada la idea de Klaas para crear una estructura ligera para medir el tiempo de carrera y el intervalo:

Uso del código:

 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 

No es necesario llamar a la función de parada, ya que la función de impresión dará el tiempo transcurrido. Se puede llamar repetidamente para obtener el tiempo transcurrido. Pero para detener el temporizador en cierto punto del código use timer.stop() , también se puede usar para devolver el tiempo en segundos: let seconds = timer.stop() Después de que se detiene el temporizador, el temporizador de intervalos no lo hará, por lo que print("Running: \(timer) ") dará la hora correcta incluso después de algunas líneas de código.

A continuación está el código para RunningTimer. Se prueba para 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"} } } 

Envuélvelo en un bloque de finalización para un uso fácil.

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

Aquí está mi bash por la respuesta más 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 

Notas

  • startTime y endTime son del tipo TimeInterval , que es solo una typealias para Double , por lo que es fácil convertirlo en Int o lo que sea. El tiempo se mide en segundos con una precisión de menos de milisegundos.
  • Consulte también DateInterval , que incluye una hora real de inicio y finalización.
  • Usar el tiempo desde 1970 es similar a las marcas de tiempo de Java .

Este es el fragmento que se me ocurrió y parece funcionar para mí en mi Macbook con Swift 4.

Nunca probado en otros sistemas, pero pensé que valía la pena compartirlo de todos modos.

 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()) } 

Aquí hay un ejemplo de cómo usarlo:

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

Otra forma es hacerlo más explícitamente:

 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") 

Así es como lo escribí.

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

Para medir un algoritmo, úsalo así.

 let time = measure { var array = [2,4,5,2,5,7,3,123,213,12] array.sorted() } print("Block is running \(time) seconds.") 

Clase estática Swift3 para temporización básica de funciones. Hará un seguimiento de cada temporizador por su nombre. Llámalo así en el momento en que quieras comenzar a medir:

 Stopwatch.start(name: "PhotoCapture") 

Llámalo para capturar e imprimir el tiempo transcurrido:

 Stopwatch.timeElapsed(name: "PhotoCapture") 

Este es el resultado: *** PhotoCapture transcurrido ms: 1402.415125 Hay un parámetro “useNanos” si desea usar nanos. Por favor, siéntase libre de cambiar según sea necesario.

  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))") } } 

}

Basado en la respuesta de Franklin Yu

Detalles

Xcode 9.4.1, Swift 4.1

Solución

 import Foundation class Measurer { 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) } } 

Muestras de uso

 // Sample with Date class var m = Measurer { Date().timeIntervalSince1970 } var time: Any = m.measure { _ = (1...1000).map{_ in Int(arc4random()%100)} } print("Date: \(time)") // 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)")