Измерение прошедшего времени в 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 для простого времени запуска.


Насколько я знаю, существует по крайней мере десять способов измерения прошедшего времени:

Монотонные часы на основе:

  1. ProcessInfo.systemUptime.
  2. mach_absolute_time с mach_timebase_info как упомянуто в этом ответе.
  3. clock() в стандарте POSIX.
  4. times() в стандарте POSIX. (Слишком сложно, так как нам нужно учитывать время пользователя против системного времени, и участвуют дочерние процессы.)
  5. DispatchTime (обертка вокруг API времени Маха), как упомянуто JeremyP в принятом ответе.
  6. CACurrentMediaTime().

Настенные часы на основе:

(никогда не используйте их для метрик: см. ниже, почему)

  1. NSDate/Date как упомянуто другими.
  2. CFAbsoluteTime как упомянуто другими.
  3. DispatchWallTime.
  4. 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)")