Zmierz upływający czas w Swift

132

Jak możemy zmierzyć czas, który upłynął do uruchomienia funkcji w języku Swift? Próbuję wyświetlić upływający czas w następujący sposób: „Upłynęło 0,05 sekundy”. Widziałem, że w Javie możemy użyć System.nanoTime (), czy w języku Swift są dostępne równoważne metody, aby to osiągnąć?

Proszę spojrzeć na przykładowy program:

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
źródło
1
nie ma to związku z twoim problemem z pomiarem czasu, ale pętla może zostać zatrzymana sqrt(number)zamiast numberi możesz zaoszczędzić trochę więcej czasu - ale jest o wiele więcej pomysłów optymalizujących wyszukiwanie liczb pierwszych.
holex
@holex Proszę zignorować użyty algorytm. Próbuję dowiedzieć się, jak możemy zmierzyć upływający czas?
Vaquita,
1
możesz używać NSDateobiektów i możesz zmierzyć różnicę między nimi.
holex
4
Jeśli używasz XCode, polecam skorzystać z nowej funkcji testowania wydajności. To nie wszystkie podnoszenia ciężkich dla Ciebie, a nawet prowadzi to wiele razy i daje średni czas z jego odchylenie standardowe ...
Roshan
1
@dumbledad Możesz mierzyć wydajność całych bloków bezpośrednio w testach jednostkowych. Na przykład zobacz to . Jeśli chcesz jeszcze bardziej rozbić przebieg (powiedzmy wiersz po kodzie linii), wypróbuj Time Profiler, który jest częścią Instruments. Jest to znacznie potężniejsze i bardziej wszechstronne.
Roshan

Odpowiedzi:

229

Oto funkcja języka Swift, którą napisałem, aby zmierzyć problemy projektu Euler w języku Swift

Od wersji Swift 3 dostępna jest teraz wersja Grand Central Dispatch, która jest „szybka”. Zatem prawidłowa odpowiedź to prawdopodobnie użycie interfejsu API DispatchTime .

Moja funkcja wyglądałaby tak:

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

Stara odpowiedź

W przypadku Swift 1 i 2 moja funkcja używa 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
}

Należy zwrócić uwagę, że używanie NSdate do funkcji czasowych jest odradzane: „ Czas systemowy może ulec skróceniu z powodu synchronizacji z zewnętrznymi odniesieniami czasu lub z powodu jawnej zmiany zegara przez użytkownika. ”.

JeremyP
źródło
Wydaje się, że daje to dobre wyniki do około +/- 2 mikrosekund. Może to jednak zależy od platformy.
user1021430
6
swift3 nie działa dla mnie, zwraca 0,0114653027, co wiem, że nie jest prawdą, ten z datami zwraca 4.77720803022385sec
Cristi Băluță
5
Niestety nie. Z jakiegoś powodu przestał działać. Mam test, który mówi, że trwał 1004959766 nanosekund (około sekundy), ale zdecydowanie działał przez około 40 sekund. Użyłem Dateobok i na pewno zgłasza 41,87 sekundy. Nie wiem, co uptimeNanosecondsrobi, ale nie zgłasza prawidłowego czasu trwania.
jowie
2
Przepraszamy za dużą edycję, nadając priorytet nowszemu rozwiązaniu, ale zalecałbym nawet całkowite usunięcie kodu NSDate: korzystanie z NSDate jest całkowicie zawodne: NSDate jest oparty na zegarze systemowym, który może się zmienić w dowolnym momencie z wielu różnych powodów, takich jak jak synchronizacja czasu sieciowego (NTP) aktualizowanie zegara (zdarza się często w celu dostosowania do dryftu), regulacje czasu letniego, sekundy przestępne i ręczna regulacja zegara przez użytkownika. Ponadto nie możesz już publikować w AppStore żadnej aplikacji używającej Swift 1: Swift 3 to absolutne minimum. Więc nie ma sensu trzymać swojego kodu NSDate inaczej niż powiedzieć „nie rób tego”.
Cœur
1
@ Wasza krytyka wersji NSDate jest słuszna (z wyjątkiem zmian czasu letniego, które nie wpłyną na NSDate, która jest zawsze w GMT), a zmiana w odwróconej kolejności jest zdecydowanie ulepszeniem.
JeremyP
69

To jest przydatna klasa timera oparta na CoreFoundations 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
        }
    }
}

Możesz go używać w ten sposób:

let timer = ParkBenchTimer()

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

println("The task took \(timer.stop()) seconds.")
Klaas
źródło
4
Powtarzające się wywołania tej funkcji nie gwarantują monotonnie rosnących wyników. ” Zgodnie z dokumentacją .
Franklin Yu
8
Więcej kontekstu: „Powtarzające się wywołania tej funkcji nie gwarantują monotonicznie rosnących wyników. Czas systemowy może ulec skróceniu z powodu synchronizacji z zewnętrznymi odniesieniami czasu lub z powodu jawnej zmiany zegara przez użytkownika ”.
Klaas
Programista powinien wziąć pod uwagę „synchronizację z zewnętrznymi odniesieniami czasowymi” i „jawną zmianę zegara przez użytkownika”. Czy chodziło Ci o to, że dwie sytuacje nie mogą się wydarzyć?
Franklin Yu
@FranklinYu nie, chciałem tylko powiedzieć, że są to dwa możliwe powody. Jeśli polegasz na ciągłym uzyskiwaniu monotonicznie rosnących wartości, istnieją inne opcje.
Klaas
czasami chcę zmierzyć krótki okres czasu; jeśli w tym czasie nastąpi synchronizacja, mogę skończyć z czasem ujemnym. Właściwie jest taki, mach_absolute_timektóry nie cierpi na ten problem, więc zastanawiam się, dlaczego nie jest on powszechnie znany. Może tylko dlatego, że nie jest dobrze udokumentowany.
Franklin Yu
54

Użyj clock, ProcessInfo.systemUptimelub DispatchTimedla prostego czasu uruchamiania.


O ile wiem, istnieje co najmniej dziesięć sposobów mierzenia upływającego czasu:

Na podstawie zegara monotonicznego:

  1. ProcessInfo.systemUptime.
  2. mach_absolute_timez mach_timebase_infojak wspomniano w tej odpowiedzi .
  3. clock()w standardzie POSIX .
  4. times()w standardzie POSIX . (Zbyt skomplikowane, ponieważ musimy rozważyć czas użytkownika w porównaniu z czasem systemowym, a procesy potomne są zaangażowane).
  5. DispatchTime (wrapper wokół Mach time API), jak wspomniał JeremyP w zaakceptowanej odpowiedzi.
  6. CACurrentMediaTime().

Na podstawie zegara ściennego:

(nigdy nie używaj ich do pomiarów: zobacz poniżej, dlaczego)

  1. NSDate/ Datejak wspominali inni.
  2. CFAbsoluteTime jak wspominali inni.
  3. DispatchWallTime.
  4. gettimeofday()w standardzie POSIX .

Opcje 1, 2 i 3 omówiono poniżej.

Opcja 1: Process Info API w Foundation

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

gdzie diff:NSTimeIntervaljest upływający czas w sekundach.

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

gdzie diff:Doublejest upływający czas o nano-sekundy.

Opcja 3: API zegara POSIX

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

gdzie diff:Doublejest upływający czas w sekundach.

Dlaczego nie czas na zegarze ściennym dla upływającego czasu?

W dokumentacji CFAbsoluteTimeGetCurrent:

Wielokrotne wywołania tej funkcji nie gwarantują monotonnie rosnących wyników.

Powód jest podobny do currentTimeMillisvs nanoTimew Javie :

Nie możesz użyć tego w innym celu. Powodem jest to, że żaden zegar komputera nie jest doskonały; zawsze dryfuje i od czasu do czasu wymaga korekty. Ta korekta może nastąpić ręcznie lub w przypadku większości komputerów działa proces, który nieustannie wprowadza niewielkie poprawki do zegara systemowego („zegar ścienny”). To zdarza się często. Kolejna taka korekta ma miejsce, gdy pojawia się sekunda przestępna.

Tutaj CFAbsoluteTimepodaje czas zegara ściennego zamiast czasu uruchomienia. NSDateto również zegar ścienny.

Franklin Yu
źródło
najlepsza odpowiedź - ale co, do diabła, jest najlepszym podejściem ?!
Fattie
1
Opcja 5 najbardziej mi odpowiadała. Jeśli rozważasz kod wieloplatformowy, wypróbuj ten. Biblioteki Darwina i Glibc mają swoje clock()funkcje.
Misha Svyatoshenko
Rozwiązań opartych zegara systemowego: Miałem sukces ProcessInfo.processInfo.systemUptime, mach_absolute_time(), DispatchTime.now()i CACurrentMediaTime(). Ale rozwiązanie z clock()nie było poprawnie konwertowane na sekundy. Dlatego radziłbym zaktualizować swoje pierwsze zdanie do: „ Użyj ProcessInfo.systemUptime lub DispatchTime, aby uzyskać dokładny czas uruchamiania.
Cœur
36

Szybka 4 najkrótsza odpowiedź:

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

Wydrukuje ci około 1.02107906341553 sekund, które upłynęły (czas oczywiście będzie się różnił w zależności od zadania, pokazuję to tylko, aby zobaczyć poziom dokładności dziesiętnej dla tego pomiaru).

Mam nadzieję, że od teraz pomoże to komuś w Swift 4!

Aktualizacja

Jeśli chcesz mieć ogólny sposób testowania fragmentów kodu, proponuję następny fragment:

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
źródło
Czy ktoś może wyjaśnić, dlaczego * -1?
Maksim Kniazev
1
@MaksimKniazev Ponieważ jest to wartość ujemna, ludzie chcą pozytywnej!
thachnb
@thachnb Nie miałem pojęcia, że ​​„startPoint.timeIntervalSinceNow” daje wartość ujemną ...
Maksim Kniazev
1
@MaksimKniazev Apple mówi, że: Jeśli obiekt daty jest wcześniejszy niż bieżąca data i godzina, wartość tej właściwości jest ujemna.
Peter Guan,
13
let start = NSDate()

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

let elapsed = start.timeIntervalSinceNow

// elapsed is a negative value.
user3370455
źródło
2
abs (start.timeIntervalSinceNow) // <- wartość dodatnia
eonist
w zasadzie najprostsze rozwiązanie, jakie widziałem, dziękuję
MiladiuM
10

Po prostu skopiuj i wklej tę funkcję. Napisane w szybkim 5. Kopiowanie JeremyP tutaj.

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

Użyj tego jak

calculateTime {
     exampleFunc()// function whose execution time to be calculated
}
ShaileshAher
źródło
6

Możesz stworzyć timefunkcję do mierzenia połączeń. Inspiruje mnie odpowiedź Klaasa .

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

Ta funkcja pozwoli ci wywołać to w ten sposób, time (isPrime(7))co zwróci krotkę zawierającą wynik i opis ciągu, który upłynął.

Jeśli chcesz tylko upływającego czasu, możesz to zrobić time (isPrime(7)).duration

Mellson
źródło
3
„Powtarzające się wywołania tej funkcji nie gwarantują monotonnie rosnących wyników”. zgodnie z dokumentacją .
Franklin Yu
3

Prosta funkcja pomocnicza do pomiaru czasu wykonania z zamknięciem.

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

Stosowanie:

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

Wynik: #Init - execution took 1.00104497105349 seconds

Vadim Akhmerov
źródło
2

możesz mierzyć nanosekundy w ten sposób:

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
źródło
Właśnie uruchomiłem to bez kodu umieszczonego w „// twoja długa procedura” i dało to wynik 240900993, co daje 0,24 sekundy.
user1021430
tworzenie instancji NSCalendarjest kosztowną procedurą, ale możesz to zrobić przed rozpoczęciem wykonywania procedury, więc nie zostanie ona dodana do czasu wykonywania Twojej długiej procedury ... pamiętaj, że wywołanie tworzenia NSDateinstancji i wywoływania –components(_:, fromDate:)metody nadal wymaga czasu, więc prawdopodobnie nigdy nie dostaniesz 0.0nanosekund.
holex
Z wyglądu konstruktora NSCalendar wynika, że ​​może nie być możliwe utworzenie go z wyprzedzeniem (startDate jest wymaganym wejściem). To trochę zaskakujące, że wygląda na taką świnię.
user1021430
możesz to zrobić (moja zaktualizowana odpowiedź), mierzony czas bez żadnej dodatkowej procedury to około 6.9mikrosekund na moim urządzeniu.
holex
Fajne dzięki. Podstawowa procedura jest teraz podobna do odpowiedzi JeremyP, ale raportowanie jest inne.
user1021430
2

Używam tego:

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

Nie wiem, czy jest bardziej czy mniej dokładny niż poprzednio opublikowany. Ale sekundy mają dużo miejsc po przecinku i wydają się wychwytywać niewielkie zmiany w kodzie w algorytmach, takich jak QuickSort przy użyciu funkcji swap (), w porównaniu z implementacją samego swapu itp.

Pamiętaj, aby zwiększyć optymalizację kompilacji podczas testowania wydajności:

Szybkie optymalizacje kompilatora

Peheje
źródło
2

Oto moja próba najprostszej odpowiedzi:

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

Uwagi

  • startTimei endTimesą typu TimeInterval, który jest po prostu typealiasfor Double, więc łatwo jest przekształcić go w Intlub cokolwiek. Czas jest mierzony w sekundach z dokładnością do milisekund.
  • Zobacz też DateInterval, który zawiera rzeczywisty czas rozpoczęcia i zakończenia.
  • Używanie czasu od 1970 roku jest podobne do znaczników czasu w Javie .
Suragch
źródło
Najprostszym sposobem, chciałby go w dwóch ciągów, druga to niech ElapsedTime = Date () timeIntervalSince1970 -. startTime
FreeGor
1

Zapożyczyłem pomysł od Klaasa, aby stworzyć lekką strukturę do pomiaru czasu biegu i interwałów:

Wykorzystanie kodu:

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

Funkcja stop nie musi być wywoływana, ponieważ funkcja drukowania poda upływ czasu. Może być wywoływane wielokrotnie, aby uzyskać upływ czasu. Ale aby zatrzymać licznik czasu w określonym punkcie kodu timer.stop(), można go również użyć do zwrócenia czasu w sekundach: let seconds = timer.stop() Po zatrzymaniu licznika czasu interwał nie będzie, więc print("Running: \(timer) ")poda prawidłowy czas nawet po kilku wierszach kodu.

Poniżej znajduje się kod RunningTimer. Jest testowany dla 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"}
    }
}
Sverrisson
źródło
1

Zapakuj go w blok uzupełniający, aby był łatwy w użyciu.

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
źródło
0

To jest fragment, który wymyśliłem i wydaje się, że działa dla mnie na moim Macbooku ze Swift 4.

Nigdy nie testowałem na innych systemach, ale pomyślałem, że i tak warto się nim podzielić.

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

Oto przykład, jak go używać:

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

Innym sposobem jest zrobienie tego bardziej szczegółowo:

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
źródło
0

Tak to napisałem.

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

Aby zmierzyć algorytm, użyj go w ten sposób.

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

print("Block is running \(time) seconds.")
BilalReffas
źródło
Jak dokładny jest ten czas? Ponieważ wcześniej używałem timera do aktualizacji co 0,1 sekundy. Porównałem tę odpowiedź i jedną z timerem i wynik z tej odpowiedzi to 0,1 sekundy to więcej niż z timerem ..
Maksim Kniazev
0

Statyczna klasa Swift3 dla podstawowego taktowania funkcji. Będzie śledzić każdy timer według nazwy. Nazwij to w ten sposób w miejscu, w którym chcesz rozpocząć pomiar:

Stopwatch.start(name: "PhotoCapture")

Wywołaj to, aby uchwycić i wydrukować czas, który upłynął:

Stopwatch.timeElapsed(name: "PhotoCapture")

Oto wynik: *** PhotoCapture upłynął ms: 1402.415125 Istnieje parametr „useNanos”, jeśli chcesz używać nanos. W razie potrzeby możesz je zmienić.

   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
źródło
Cała praca z mach_timebase_infojuż wdrożone lepiej niż ty w kodzie źródłowym z ProcessInfo.processInfo.systemUptime. Więc po prostu zrób watches[name] = ProcessInfo.processInfo.systemUptime. A * TimeInterval(NSEC_PER_MSEC)jeśli chcesz nanos.
Cœur
0

Na podstawie odpowiedzi Franklina Yu i Cœura komentarzy

Detale

  • Xcode 10.1 (10B61)
  • Swift 4.2

Rozwiązanie 1

pomiar(_:)

Rozwiązanie 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)
    }
}

Zastosowanie rozwiązania 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)")
Wasilij Bodnarchuk
źródło
Nie jestem pewien, dlaczego mielibyśmy używać implementacji zmiennych. Używanie Datedo mierzenia czegokolwiek jest bardzo odradzane (ale systemUptimelub clock()jest OK). Jeśli chodzi o testy, to measure(_:)już mamy .
Cœur
1
Kolejna uwaga: dlaczego zamknięcie jest opcjonalne?
Cœur
@ Cœur you right! Dziękuję za uwagi. Zaktualizuję post później.
Wasilij Bodnarchuk