Messen Sie die verstrichene Zeit in Swift

132

Wie können wir die Zeit messen, die zum Ausführen einer Funktion in Swift vergangen ist? Ich versuche, die verstrichene Zeit folgendermaßen anzuzeigen: "Die verstrichene Zeit beträgt 0,05 Sekunden". Saw , dass in Java , können wir System.nanoTime () verwenden, ist es gleichwertige Verfahren in Swift verfügbar sind , dies zu erreichen?

Bitte schauen Sie sich das Beispielprogramm an:

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");
}
Vaquita
quelle
1
Es hat nichts mit Ihrem Problem der Zeitmessung zu tun, aber die Schleife kann stattdessen gestoppt sqrt(number)werden number, und Sie können etwas mehr Zeit sparen - aber es gibt viel mehr Ideen zur Optimierung der Suche nach Primzahlen.
Holex
@holex Bitte ignorieren Sie den verwendeten Algorithmus. Ich versuche herauszufinden, wie wir die verstrichene Zeit messen können.
Vaquita
1
Sie können NSDateObjekte verwenden und den Unterschied zwischen ihnen messen.
Holex
4
Wenn Sie XCode verwenden, empfehle ich Ihnen, die neue Funktion zum Testen der Leistung zu verwenden. Es erledigt das ganze schwere Heben für Sie und führt es sogar mehrmals aus und gibt Ihnen die durchschnittliche Zeit mit seiner Standardabweichung ...
Roshan
1
@dumbledad Sie können die Leistung ganzer Blöcke direkt in Unit-Tests messen. Siehe zum Beispiel dies . Wenn Sie einen Run-Down weiter aufteilen möchten (z. B. zeilenweise Code), lesen Sie den Time Profiler, der Teil von Instruments ist. Dies ist viel leistungsfähiger und umfassender.
Roshan

Antworten:

228

Hier ist eine Swift-Funktion, die ich geschrieben habe, um Project Euler- Probleme in Swift zu messen

Ab Swift 3 gibt es jetzt eine Version von Grand Central Dispatch, die "schnell" ist. Die richtige Antwort ist also wahrscheinlich die Verwendung der DispatchTime-API .

Meine Funktion würde ungefähr so ​​aussehen:

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

Alte Antwort

Für Swift 1 und 2 verwendet meine Funktion 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
}

Beachten Sie, dass von der Verwendung von NSdate für Zeitsteuerungsfunktionen abgeraten wird: " Die Systemzeit kann sich aufgrund der Synchronisation mit externen Zeitreferenzen oder aufgrund einer expliziten Änderung der Uhr durch den Benutzer verringern. "

JeremyP
quelle
Dies scheint gute Ergebnisse bis zu etwa +/- 2 Mikrosekunden zu liefern. Möglicherweise variiert dies jedoch je nach Plattform.
user1021430
6
swift3 funktioniert bei mir nicht, es gibt 0.0114653027 zurück, was ich für eine Tatsache weiß, die nicht wahr ist. Die mit Datumsangaben gibt 4.77720803022385sec zurück
Cristi Băluță
5
Leider nicht. Die Betriebszeit ist aus irgendeinem Grund komplett aus. Ich habe einen Test, der besagt, dass er 1004959766 Nanosekunden (ungefähr eine Sekunde) gedauert hat, aber definitiv ungefähr 40 Sekunden lang lief. Ich habe Datenebenbei verwendet, und zwar 41,87 Sekunden. Ich weiß nicht, was uptimeNanosecondses tut, aber es wird nicht die richtige Dauer angegeben.
Jowie
2
Entschuldigen Sie die große Bearbeitung, bei der Ihre neuere Lösung priorisiert wird, aber ich würde sogar empfehlen, Ihren NSDate-Code vollständig zu löschen: Die Verwendung von NSDate ist völlig unzuverlässig: NSDate basiert auf der Systemuhr, die sich aus vielen verschiedenen Gründen jederzeit ändern kann, z Als NTP (Network Time Sync) wird die Uhr aktualisiert (häufig wird die Drift angepasst), die Sommerzeit wird angepasst, Schaltsekunden und die Uhr wird manuell eingestellt. Außerdem können Sie mit Swift 1 keine App mehr im AppStore veröffentlichen: Swift 3 ist das absolute Minimum. Es macht also keinen Sinn, Ihren NSDate-Code beizubehalten, außer zu sagen: "Tu es nicht".
Cœur
1
@ Cœur Ihre Kritik an der NSDate-Version ist gültig (außer DST-Änderungen wirken sich nicht auf NSDate aus, das immer in GMT ist), und Ihre Bearbeitung, um die Reihenfolge umzukehren, ist definitiv eine Verbesserung.
JeremyP
69

Dies ist eine praktische Timer-Klasse, die auf CoreFoundations basiert 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
        }
    }
}

Sie können es so verwenden:

let timer = ParkBenchTimer()

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

println("The task took \(timer.stop()) seconds.")
Klaas
quelle
4
Wiederholte Anrufe an diese Funktion übernehmen keine Garantie monoton Ergebnisse zu erhöhen. “ Gemäß seiner Dokumentation .
Franklin Yu
8
Mehr Kontext: "Wiederholte Aufrufe dieser Funktion garantieren keine monoton steigenden Ergebnisse. Die Systemzeit kann sich aufgrund der Synchronisation mit externen Zeitreferenzen oder aufgrund einer expliziten Änderung der Uhr durch den Benutzer verringern ."
Klaas
Entwickler sollten "Synchronisation mit externen Zeitreferenzen" und "explizite Benutzeränderung der Uhr" berücksichtigen. Meinten Sie, dass es unmöglich ist, dass die beiden Situationen eintreten?
Franklin Yu
@FranklinYu nein, ich wollte nur sagen, dass dies die beiden möglichen Gründe sind. Wenn Sie darauf angewiesen sind, immer monoton ansteigende Werte zu erhalten, gibt es andere Möglichkeiten.
Klaas
manchmal möchte ich einen kurzen Zeitraum messen; Wenn währenddessen eine Synchronisation stattfindet, kann dies zu einer negativen Zeit führen. Es gibt tatsächlich mach_absolute_timediejenigen, die nicht unter diesem Problem leiden, also frage ich mich, warum dieses nicht allgemein bekannt ist. Vielleicht nur, weil es nicht gut dokumentiert ist.
Franklin Yu
54

Verwenden Sie clock, ProcessInfo.systemUptimeoder DispatchTimefür eine einfache Startzeit.


Soweit ich weiß, gibt es mindestens zehn Möglichkeiten, die verstrichene Zeit zu messen:

Monotone Uhr basiert:

  1. ProcessInfo.systemUptime.
  2. mach_absolute_timemit mach_timebase_infowie in dieser Antwort erwähnt .
  3. clock()im POSIX-Standard .
  4. times()im POSIX-Standard . (Zu kompliziert, da wir Benutzerzeit und Systemzeit berücksichtigen müssen und untergeordnete Prozesse beteiligt sind.)
  5. DispatchTime (ein Wrapper um die Mach-Zeit-API), wie von JeremyP in der akzeptierten Antwort erwähnt.
  6. CACurrentMediaTime().

Wanduhr basiert:

(Verwenden Sie diese niemals für Metriken: siehe unten, warum)

  1. NSDate/ Datewie von anderen erwähnt.
  2. CFAbsoluteTime wie von anderen erwähnt.
  3. DispatchWallTime.
  4. gettimeofday()im POSIX-Standard .

Option 1, 2 und 3 werden nachstehend erläutert.

Option 1: Verarbeiten der Info-API in Foundation

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

Wo diff:NSTimeIntervalist die verstrichene Zeit in Sekunden.

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

Wo diff:Doubleist die verstrichene Zeit in Nanosekunden?

Option 3: POSIX Clock API

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

Wo diff:Doubleist die verstrichene Zeit in Sekunden.

Warum nicht die Wanduhrzeit für die verstrichene Zeit?

In der Dokumentation von CFAbsoluteTimeGetCurrent:

Wiederholte Aufrufe dieser Funktion garantieren keine monoton steigenden Ergebnisse.

Grund ist ähnlich wie currentTimeMillisvs nanoTimein Java :

Sie können das eine nicht für den anderen Zweck verwenden. Der Grund ist, dass keine Computeruhr perfekt ist; es driftet immer und muss gelegentlich korrigiert werden. Diese Korrektur kann entweder manuell erfolgen, oder bei den meisten Maschinen wird ein Prozess ausgeführt, der kontinuierlich kleine Korrekturen an der Systemuhr ("Wanduhr") vornimmt. Diese treten häufig auf. Eine weitere solche Korrektur erfolgt immer dann, wenn eine Schaltsekunde vorliegt.

Hier wird CFAbsoluteTimedie Wanduhrzeit anstelle der Startzeit angegeben. NSDateist auch Wanduhrzeit.

Franklin Yu
quelle
die beste Antwort - aber was zum Teufel ist der beste Ansatz?!
Fattie
1
Option 5 passte am besten zu mir. Wenn Sie Multi-Plattform-Code in Betracht ziehen, versuchen Sie diesen. Sowohl Darwin- als auch Glibc-Bibliotheken haben clock()Funktion.
Mischa Svyatoshenko
Für die Systemuhr basierten Lösungen: Ich hatte Erfolg mit ProcessInfo.processInfo.systemUptime, mach_absolute_time(), DispatchTime.now()und CACurrentMediaTime(). Aber die Lösung mit clock()wurde nicht richtig in Sekunden konvertiert. Daher würde ich empfehlen, Ihren ersten Satz zu aktualisieren: " Verwenden Sie ProcessInfo.systemUptime oder DispatchTime für eine genaue Startzeit. "
Cœur
36

Swift 4 kürzeste Antwort:

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

Es druckt Ihnen ungefähr 1.02107906341553 Sekunden (die Zeit variiert natürlich je nach Aufgabe, ich zeige dies nur, damit Sie die Dezimalgenauigkeit für diese Messung sehen können).

Hoffe, es hilft jemandem in Swift 4 von nun an!

Aktualisieren

Wenn Sie eine generische Methode zum Testen von Codeteilen haben möchten, würde ich das nächste Snippet vorschlagen:

func measureTime(for closure: @autoclosure () -> Any) {
    let start = CFAbsoluteTimeGetCurrent()
    closure()
    let diff = CFAbsoluteTimeGetCurrent() - start
    print("Took \(diff) seconds")
}

*Usage*

measureTime(for: <insert method signature here>)

**Console log**
Took xx.xxxxx seconds
Mauricio Chirino
quelle
Kann jemand erklären warum * -1?
Maksim Kniazev
1
@MaksimKniazev Weil es ein negativer Wert ist, wollen die Leute einen positiven!
Thachnb
@thachnb Ich hatte keine Ahnung, dass "StartingPoint.timeIntervalSinceNow" einen negativen Wert erzeugt ...
Maksim Kniazev
1
@MaksimKniazev Apple sagt Folgendes: Wenn das Datumsobjekt vor dem aktuellen Datum und der aktuellen Uhrzeit liegt, ist der Wert dieser Eigenschaft negativ.
Peter Guan
13
let start = NSDate()

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

let elapsed = start.timeIntervalSinceNow

// elapsed is a negative value.
user3370455
quelle
2
abs (start.timeIntervalSinceNow) // <- positiver Wert
Eonist
So ziemlich die einfachste Lösung, die ich je gesehen habe, danke
MiladiuM
10

Kopieren Sie diese Funktion einfach und fügen Sie sie ein. Geschrieben in Swift 5. Kopieren Sie JeremyP hier.

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

Verwenden Sie es wie

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

Sie können eine timeFunktion zum Messen Ihrer Anrufe erstellen . Ich bin inspiriert von Klaas ' Antwort.

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

Mit dieser Funktion können Sie es so aufrufen, dass time (isPrime(7))ein Tupel mit dem Ergebnis und einer Zeichenfolgenbeschreibung der verstrichenen Zeit zurückgegeben wird.

Wenn Sie nur die verstrichene Zeit wünschen, können Sie dies tun time (isPrime(7)).duration

Mellson
quelle
3
"Wiederholte Aufrufe dieser Funktion garantieren keine monoton steigenden Ergebnisse." laut Dokumentation .
Franklin Yu
3

Einfache Hilfsfunktion zur Messung der Ausführungszeit mit Verschluss.

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

Verwendung:

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

Ergebnis: #Init - execution took 1.00104497105349 seconds

Vadim Akhmerov
quelle
2

Sie können die Nanosekunden wie folgt messen :

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)")
holex
quelle
Ich habe dies nur ausgeführt, ohne dass Code in "// your long procedure" eingefügt wurde, und es ergab sich ein Ergebnis von 240900993, was 0,24 Sekunden entspricht.
user1021430
Das Instanziieren einer NSCalendarProzedur ist teuer, aber Sie können dies tun, bevor Sie mit der Ausführung Ihrer Prozedur beginnen, damit sie nicht zur Laufzeit Ihrer langen Prozedur hinzugefügt wird. Denken Sie daran, dass das Aufrufen des Erstellens einer NSDateInstanz und das Aufrufen der –components(_:, fromDate:)Methode immer noch Zeit in Anspruch nimmt Sie werden nie 0.0Nanosekunden bekommen .
Holex
Aus der Sicht des NSCalendar-Konstruktors geht hervor, dass es möglicherweise nicht möglich ist, ihn vorab zu erstellen (startDate ist eine erforderliche Eingabe). Es ist ein bisschen überraschend, dass es so ein Schwein zu sein scheint.
user1021430
Sie können dies tun (meine aktualisierte Antwort), die gemessene Zeit ohne zusätzliche Prozedur beträgt ungefähr 6.9Mikrosekunden auf meinem Gerät.
Holex
Cool, danke. Das grundlegende Verfahren ähnelt jetzt der Antwort von JeremyP, die Berichterstattung ist jedoch anders.
user1021430
2

Ich benutze das:

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

Ich weiß nicht, ob es mehr oder weniger genau ist als zuvor veröffentlicht. Aber die Sekunden haben viele Dezimalstellen und scheinen kleine Codeänderungen in Algorithmen wie QuickSort mit swap () oder der Implementierung von swap urself usw. zu erfassen.

Denken Sie daran, Ihre Build-Optimierungen zu verbessern, wenn Sie die Leistung testen:

Schnelle Compiler-Optimierungen

Peheje
quelle
2

Hier ist mein Versuch für die einfachste Antwort:

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

Anmerkungen

  • startTimeund endTimesind von der Art TimeInterval, die nur ein typealiasfür ist Double, so ist es einfach, es in eine Intoder was auch immer umzuwandeln . Die Zeit wird in Sekunden mit einer Genauigkeit von weniger als einer Millisekunde gemessen.
  • Siehe auch DateInterval, einschließlich einer tatsächlichen Start- und Endzeit.
  • Die Verwendung der Zeit seit 1970 ähnelt Java-Zeitstempeln .
Suragch
quelle
Der einfachste Weg, möchte es in zwei Strings, der zweite ist elapsedTime = Date (). timeIntervalSince1970 - startTime
FreeGor
1

Ich habe mir die Idee von Klaas geliehen, eine leichte Struktur zum Messen der Laufzeit und der Intervallzeit zu erstellen:

Code-Verwendung:

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

Die Stoppfunktion muss nicht aufgerufen werden, da die Druckfunktion die verstrichene Zeit angibt. Es kann wiederholt aufgerufen werden, um die abgelaufene Zeit zu erhalten. Um den Timer an einem bestimmten Punkt in der Code-Verwendung anzuhalten timer.stop(), kann er auch verwendet werden, um die Zeit in Sekunden zurückzugeben: let seconds = timer.stop() Nach dem Anhalten des Timers wird der Intervall-Timer nicht mehr angezeigt , sodass print("Running: \(timer) ")auch nach einigen Codezeilen die richtige Zeit angezeigt wird .

Es folgt der Code für RunningTimer. Es ist für Swift 2.1 getestet:

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"}
    }
}
Sverrisson
quelle
1

Wickeln Sie es zur einfachen Verwendung in einen Fertigstellungsblock.

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

Dies ist der Ausschnitt, den ich mir ausgedacht habe und der auf meinem Macbook mit Swift 4 zu funktionieren scheint.

Nie auf anderen Systemen getestet, aber ich dachte, es lohnt sich trotzdem zu teilen.

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

Hier ist ein Beispiel für die Verwendung:

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

Eine andere Möglichkeit besteht darin, dies expliziter zu tun:

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")
hasen
quelle
0

So habe ich es geschrieben.

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

Um einen Algorithmus zu messen, verwenden Sie ihn so.

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

print("Block is running \(time) seconds.")
BilalReffas
quelle
Wie genau ist diese Zeit? Weil ich vorher den Timer benutzt habe, um alle 0,1 Sekunden zu aktualisieren. Ich habe diese Antwort und eine mit Timer verglichen und das Ergebnis dieser Antwort ist 0,1 Sekunden ist mehr als mit Timer.
Maksim Kniazev
0

Statische Swift3-Klasse für das Timing grundlegender Funktionen. Jeder Timer wird anhand seines Namens verfolgt. Nennen Sie es an dem Punkt, an dem Sie mit der Messung beginnen möchten, so:

Stopwatch.start(name: "PhotoCapture")

Rufen Sie dies auf, um die verstrichene Zeit zu erfassen und auszudrucken:

Stopwatch.timeElapsed(name: "PhotoCapture")

Dies ist die Ausgabe: *** PhotoCapture abgelaufen ms: 1402.415125 Es gibt einen "useNanos" -Parameter, wenn Sie Nanos verwenden möchten. Bitte zögern Sie nicht, nach Bedarf zu ändern.

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

}}

Gjchoza
quelle
Ihre gesamte Arbeit mit mach_timebase_infoist bereits besser implementiert als im Quellcode von ProcessInfo.processInfo.systemUptime. Also einfach tun watches[name] = ProcessInfo.processInfo.systemUptime. Und * TimeInterval(NSEC_PER_MSEC)wenn Sie Nanos wollen.
Cœur
0

Basierend auf Franklin Yu Antwort und Cœur Kommentaren

Einzelheiten

  • Xcode 10.1 (10B61)
  • Swift 4.2

Lösung 1

messen(_:)

Lösung 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)
    }
}

Verwendung der Lösung 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)")
Wassili Bodnarchuk
quelle
Ich bin mir nicht sicher, warum wir variable Implementierungen verwenden müssten. Es Datewird dringend davon abgeraten, etwas zu messen (aber systemUptimeoder clock()sind in Ordnung). Tests haben wir measure(_:)bereits.
Cœur
1
Noch ein Hinweis: Warum sollte der Verschluss optional sein?
Cœur
@ Cœur du recht! Danke für deine Kommentare. Ich werde den Beitrag später aktualisieren.
Vasily Bodnarchuk