Измерение прошедшего времени в Swift
Как мы можем измерить время, затраченное на запуск функции в Swift? Я пытаюсь отобразить истекшее время следующим образом: "Истекшее время составляет 0,05 секунды". Видел, что в Java мы можем использовать System.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");
}
Ответы
Ответ 1
Вот функция Swift, которую я написал для измерения задач Project Euler в Swift
Что касается Swift 3, то теперь есть версия Grand Central Dispatch, которая "сглажена". Таким образом, правильный ответ, вероятно, заключается в использовании 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
}
Старый ответ
Для Swift 1 и 2 моя функция использует 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
}
Обратите внимание, что использование NSdate для функций синхронизации не рекомендуется: "Системное время может уменьшиться из-за синхронизации с внешними временными привязками или из-за явной смены часов пользователем".
Ответ 2
Это удобный класс таймера на основе 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
}
}
}
Вы можете использовать его следующим образом:
let timer = ParkBenchTimer()
// ... a long runnig task ...
println("The task took \(timer.stop()) seconds.")
Ответ 3
Используйте clock
, ProcessInfo.systemUptime
или DispatchTime
для простого времени запуска.
Насколько я знаю, существует по крайней мере десять способов измерения прошедшего времени:
Монотонные часы на основе:
-
ProcessInfo.systemUptime
. -
mach_absolute_time
с mach_timebase_info
как упомянуто в этом ответе. -
clock()
в стандарте POSIX. -
times()
в стандарте POSIX. (Слишком сложно, так как нам нужно учитывать время пользователя против системного времени, и участвуют дочерние процессы.) -
DispatchTime
(обертка вокруг API времени Маха), как упомянуто JeremyP в принятом ответе. -
CACurrentMediaTime()
.
Настенные часы на основе:
(никогда не используйте их для метрик: см. ниже, почему)
-
NSDate
/Date
как упомянуто другими. -
CFAbsoluteTime
как упомянуто другими. -
DispatchWallTime
. -
gettimeofday()
в стандарте POSIX.
Варианты 1, 2 и 3 подробно описаны ниже.
Вариант 1: API обработки информации в основании
do {
let info = ProcessInfo.processInfo
let begin = info.systemUptime
// do something
let diff = (info.systemUptime - begin)
}
где diff:NSTimeInterval
- прошедшее время в секундах.
Вариант 2: 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
- истекшее время на нано-секунды.
Вариант 3: API часов POSIX
do {
let begin = clock()
// do something
let diff = Double(clock() - begin) / Double(CLOCKS_PER_SEC)
}
где diff:Double
- прошедшее время в секундах.
Почему бы не настенные часы за прошедшее время?
В документации CFAbsoluteTimeGetCurrent
:
Повторные вызовы этой функции не гарантируют монотонно увеличивающиеся результаты.
Причина аналогична currentTimeMillis
против nanoTime
в Java:
Вы не можете использовать один для другой цели. Причина в том, что никакие компьютерные часы не идеальны; это всегда дрейфует и иногда должно быть исправлено. Это исправление может происходить либо вручную, либо в случае большинства машин существует процесс, который выполняется и постоянно выдает небольшие исправления системным часам ("настенным часам"). Это часто случается. Другое такое исправление происходит всякий раз, когда есть високосная секунда.
Здесь CFAbsoluteTime
предоставляет время настенных часов вместо времени запуска. NSDate
- это время настенных часов.
Ответ 4
Swift 4 кратчайший ответ:
let startingPoint = Date()
// ... intensive task
print("\(startingPoint.timeIntervalSinceNow * -1) seconds elapsed")
Он выдаст вам что-то около 1,02107906341553 секунд (время, конечно, будет меняться в зависимости от задачи, я просто показываю это для вас, ребята, чтобы увидеть уровень десятичной точности для этого измерения).
Надеюсь, это поможет кому-то в Swift 4 отныне!
Ответ 5
let start = NSDate()
for index in 1...10000 {
// do nothing
}
let elapsed = start.timeIntervalSinceNow
// elapsed is a negative value.
Ответ 6
Вы можете создать функцию time
для измерения ваших вызовов.
Я вдохновлен ответом Klaas.
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
Ответ 7
Простая вспомогательная функция для измерения времени выполнения с закрытием.
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
Ответ 8
вы можете измерить наносекунды, например, например. это:
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)")
Ответ 9
Я использую это:
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 и т.д.
Не забудьте проверить эффективность сборки при тестировании производительности:
![Swift compiler optimizations]()
Ответ 10
Вот моя попытка простейшего ответа:
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.
Ответ 11
Я заимствовал идею от 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"}
}
}
Ответ 12
Оберните его в блок завершения для удобства использования.
public class func secElapsed(completion: () -> Void) {
let startDate: NSDate = NSDate()
completion()
let endDate: NSDate = NSDate()
let timeInterval: Double = endDate.timeIntervalSinceDate(startDate)
println("seconds: \(timeInterval)")
}
Ответ 13
Просто скопируйте и вставьте эту функцию. Написано в swift 5. Копирование JeremyP здесь.
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")
}
Используйте это как
calculateTime {
exampleFunc()// function whose execution time to be calculated
}
Ответ 14
Это фрагмент, который я придумал, и, похоже, он работает на моем 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")
Ответ 15
Вот как я это написал.
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.")
Ответ 16
Статический класс 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))")
}
}
}
Ответ 17
Основано на ответе Франклина Ю и комментариях Кер
подробности
- Xcode 10.1 (10B61)
- Swift 4.2
Решение 1
измерения (_ :)
Решение 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)
}
}
Использование решения 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)")