Измерение прошедшего времени в swift
Как мы можем измерить время, прошедшее для запуска функции в Swift? Я пытаюсь отобразить прошедшее время следующим образом: "эластичное время есть .05 секунд". Увидел, что в Java, мы можем использовать систему.nanoTime (), есть ли какие-либо эквивалентные методы методы доступны в Swift для достижения этой цели?
пожалуйста, взгляните на пример программы:
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");
}
16 ответов:
вот быстрая функция, которую я написал для измерения Проект Эйлера проблемы в Swift
Он использует 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 }обновление
по состоянию на Swift 3, в настоящее время существует версия Grand Central Dispatch, которая является "swifitfied". Так что новый правильный ответ, вероятно, использовать DispatchTime API.
моя функция будет выглядеть примерно так (на самом деле не испытывают или что угодно):
// 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 }
это удобный класс таймера на основе
CoreFoundationsCFAbsoluteTime: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 } } }вы можете использовать его как это:
let timer = ParkBenchTimer() // ... a long runnig task ... println("The task took \(timer.stop()) seconds.")
использовать
clock,ProcessInfo.systemUptimeилиDispatchTimeдля простого запуска.
есть, насколько я знаю, по крайней мере, семь способов измерения времени:
NSDateкак говорили другие.CFAbsoluteTimeкак говорили другие.ProcessInfo.systemUptime.mach_absolute_timeСmach_timebase_infoкак говорится в этот ответ.clock()in POSIX standard.times()на POSIX standard. (Слишком сложно, так как нам нужно для рассмотрения пользовательского времени v. s. системное время и дочерние процессы являются вовлеченный.)DispatchTime(обертка вокруг Mach time API), как упоминалось JeremyP в принятом ответе.варианты 3, 4 и 5 подробно описаны ниже.
Вариант 3: API информации о процессе в Foundation
do { let info = ProcessInfo.processInfo let begin = info.systemUptime // do something let diff = (info.systemUptime - begin) }здесь
diff:NSTimeIntervalпрошедшее время по секундам.Вариант 4: Mach C API
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- это затраченное время на нано-секунды.Вариант 5: POSIX clock API
do { let begin = clock() // do something let diff = Double(clock() - begin) / Double(CLOCKS_PER_SEC) }здесь
diff:Double- это затраченное время в секундах.почему бы не Настенные часы время за истекшее время?
в документации
CFAbsoluteTimeGetCurrent:повторные вызовы этой функции не гарантируют монотонно возрастающий результаты.
причина похожа на
currentTimeMillisvsnanoTimeв Java:вы не можете использовать один для другой цели. Причина в том, что нет часы компьютера идеальны; он всегда дрейфует и иногда это нужно исправить. Эта коррекция может произойти вручную, или в случае большинства машин, есть процесс, который запускает и постоянно выдает небольшие исправления в системные часы ("настенные часы.)" Эти, как правило случаться часто. Еще одна такая поправка происходит всякий раз, когда есть скачкообразная секунда.
здесь
CFAbsoluteTimeобеспечивает время настенных часов вместо запуска время.NSDateвероятно настенные часы время.
let start = NSDate() for index in 1...10000 { // do nothing } let elapsed = start.timeIntervalSinceNow // elapsed is a negative value.
Swift 4 самый короткий ответ:
let startingPoint = Date() // ... intensive task print("\(startingPoint.timeIntervalSinceNow * -1) seconds elapsed")он напечатает вам что-то вроде 1.02107906341553 секунд, прошедших (время, конечно, будет меняться в зависимости от задачи, я просто показываю это для вас, ребята, чтобы увидеть уровень десятичной точности для этого измерения).
надеюсь, что это поможет кому-то в Swift 4 с этого момента!
вы можете создать
timeфункция для измерения вызовов. Меня вдохновляет Клаас' ответ.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.") }эта функция позволит вам называть его так
time (isPrime(7)), которая возвращает кортеж, содержащий результат и строковое описание прошедшего времени.если вы хотите только прошедшее время вы можете сделать это
time (isPrime(7)).duration
вы можете измерить наносекунд как например это:
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)")
Я использую этот:
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_; } }Я не знаю, если это более или менее точный, чем выложенные ранее. Но секунды имеют много десятичных знаков и, похоже, улавливают небольшие изменения кода в алгоритмах, таких как QuickSort с использованием swap() против реализации swap urself и т. д.
Не забудьте провернуть оптимизацию сборки при тестировании производительности:
простая вспомогательная функция для измерения времени выполнения с закрытием.
func printExecutionTime(withTag tag: String, of closure: () -> ()) { let start = CACurrentMediaTime() closure() print("#\(tag) - execution took \(CACurrentMediaTime() - start) seconds") }использование:
printExecutionTime(withTag: "Init") { // Do your work here }результат:
#Init - execution took 1.00104497105349 seconds
я позаимствовал идею у Klaas, чтобы создать легкую структуру для измерения времени работы и интервала:
Код Использование:
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функция остановки не должна быть вызвана, так как функция печати даст истекшее время. Он может быть вызван повторно, чтобы получить время истекло. Но чтобы остановить таймер в определенный момент в коде используйте
timer.stop()Он также может быть использован для возврата времени в секундах:let seconds = timer.stop()После того как таймер остановлен таймер интервала не будет, так чтоprint("Running: \(timer) ")дадут правильное время даже после нескольких строк кода.Ниже приведен код для RunningTimer. Он тестируется на 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"} } }
оберните его в блок завершения для удобства использования.
public class func secElapsed(completion: () -> Void) { let startDate: NSDate = NSDate() completion() let endDate: NSDate = NSDate() let timeInterval: Double = endDate.timeIntervalSinceDate(startDate) println("seconds: \(timeInterval)") }
вот моя попытка для самого простого ответа:
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Примечания
startTimeиendTimeтипаTimeInterval, который простоtypealiasнаDouble, поэтому его легко преобразовать вIntили что-то еще. Время измеряется в секундах с точностью до миллисекунд.- см. также
DateInterval, который включает в себя фактическое время начала и окончания.- использование времени с 1970 года является похоже на метки времени Java.
Это фрагмент я придумал, и это, кажется, работает для меня на моем Macbook с Swift 4.
никогда не испытанное на других системах, но я думал, что это стоит поделиться.
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()) }вот пример того, как его использовать:
let t1 = monotonic_now() // .. some code to run .. let elapsed = seconds_elapsed(since: t1) print("Time elapsed: \(elapsed*1000)ms")другой способ сделать это более явно:
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")
вот как я это написал.
func measure<T>(task: () -> T) -> Double { let startTime = CFAbsoluteTimeGetCurrent() task() let endTime = CFAbsoluteTimeGetCurrent() let result = endTime - startTime return result }для измерения алгоритм использовать это так.
let time = measure { var array = [2,4,5,2,5,7,3,123,213,12] array.sorted() } print("Block is running \(time) seconds.")
статический класс Swift3 для базовой функции синхронизации. Он будет отслеживать каждый таймер по имени. Назовите это так в точке, которую вы хотите начать измерять:
Stopwatch.start(name: "PhotoCapture")вызовите это, чтобы захватить и распечатать время, прошедшее:
Stopwatch.timeElapsed(name: "PhotoCapture")это выход: *** PhotoCapture прошедшее МС: 1402.415125 Существует параметр" useNanos", если вы хотите использовать nanos. Пожалуйста, не стесняйтесь менять по мере необходимости.
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))") } }}
на основе Франклин Ю ответ
подробности
Xcode 9.4.1, Swift 4.1
решение
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) } }примеры использования
// 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()) - ) / Double(CLOCKS_PER_SEC) } time = m.measure { _ = (1...1000).map{_ in Int(arc4random()%100)} } print("POSIX: \(time)")

Comments