developer tip

Swift에서 경과 시간 측정

optionbox 2020. 8. 8. 12:29
반응형

Swift에서 경과 시간 측정


Swift에서 함수를 실행하는 데 걸린 시간을 어떻게 측정 할 수 있습니까? 다음과 같이 경과 시간을 표시하려고합니다. "경과 시간은 .05 초입니다". 톱 있음을 자바로 , 우리는 System.nanoTime ()를 사용하여 동등한 방법은 이러한 목표를 달성하기 위해 스위프트에서 사용할 수 있습니다 무엇입니까?

샘플 프로그램을 살펴보십시오 :

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

다음은 Swift 에서 Project Euler 문제 를 측정하기 위해 작성한 Swift 함수입니다.

Swift 3부터는 "swiftified"버전의 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를 사용하는 것은 권장되지 않습니다. " 외부 시간 참조와의 동기화 또는 시계의 명시적인 사용자 변경으로 인해 시스템 시간이 감소 할 수 있습니다. ".


이것은 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
        }
    }
}

다음과 같이 사용할 수 있습니다.

let timer = ParkBenchTimer()

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

println("The task took \(timer.stop()) seconds.")

간단한 시작 시간 clock에는 ProcessInfo.systemUptime, 또는를 사용하십시오 DispatchTime.


내가 아는 한, 경과 시간을 측정하는 방법은 최소한 10 가지입니다.

Monotonic Clock 기반 :

  1. ProcessInfo.systemUptime.
  2. mach_absolute_timemach_timebase_info에서 언급 한 바와 같이 이 답변 .
  3. clock()에서 POSIX 표준 .
  4. times()에서 POSIX 표준 . (사용자 시간과 시스템 시간을 고려해야하고 하위 프로세스가 관련되어 있으므로 너무 복잡합니다.)
  5. DispatchTime (Mach time API를 둘러싼 래퍼) JeremyP가 수락 한 답변에서 언급했습니다.
  6. CACurrentMediaTime().

벽시계 기반 :

(측정 항목에는 절대 사용하지 마세요. 아래 이유를 참조하세요.

  1. NSDate/ Date다른 사람들이 언급했듯이.
  2. CFAbsoluteTime 다른 사람들이 언급했듯이.
  3. DispatchWallTime.
  4. gettimeofday()에서 POSIX 표준 .

옵션 1, 2 및 3은 아래에 자세히 설명되어 있습니다.

옵션 1 : Foundation의 Process Info 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 : POSIX 클록 API

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

여기서 diff:Double초 의해 경과 시간이다.

경과 시간에 대한 벽시계 시간이 아닌 이유는 무엇입니까?

문서에서 CFAbsoluteTimeGetCurrent:

이 함수에 대한 반복적 인 호출은 단조롭게 증가하는 결과를 보장하지 않습니다.

이유는 Java의 currentTimeMillisvsnanoTime 와 유사합니다 .

다른 용도로 사용할 수 없습니다. 그 이유는 컴퓨터의 시계가 완벽하지 않기 때문입니다. 항상 표류하며 때때로 수정해야합니다. 이 수정은 수동으로 이루어 지거나 대부분의 컴퓨터에서 실행되고 시스템 시계 ( "벽 시계")에 대한 작은 수정을 지속적으로 발행하는 프로세스가 있습니다. 이것은 자주 일어나는 경향이 있습니다. 또 다른 수정은 윤초가있을 때마다 발생합니다.

여기 CFAbsoluteTime에서는 시작 시간 대신 벽시계 시간을 제공합니다. NSDate벽시계 시간이기도합니다.


Swift 4 짧은 답변 :

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

1.02107906341553 초가 경과 한 것과 같은 것을 인쇄 할 것 입니다 (물론 시간은 작업에 따라 다를 수 있습니다.이 측정에 대한 십진 정밀도 수준을 볼 수 있도록 보여 드리겠습니다).

지금부터 Swift 4의 누군가에게 도움이되기를 바랍니다!


let start = NSDate()

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

let elapsed = start.timeIntervalSinceNow

// elapsed is a negative value.

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


클로저로 실행 시간을 측정하는 간단한 도우미 기능.

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


다음 과 같이 나노초 를 측정 할 수 있습니다 .

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

나는 그것이 이전에 게시 된 것보다 다소 정확한지 모르겠습니다. 그러나 초는 십진수가 많고 swap ()을 사용하는 QuickSort와 같은 알고리즘에서 작은 코드 변경 사항을 포착하는 것 같습니다.

성능을 테스트 할 때 빌드 최적화를 강화하는 것을 잊지 마십시오.

Swift 컴파일러 최적화


가장 간단한 대답을위한 나의 시도는 다음과 같습니다.

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

노트

  • startTimeendTime유형 TimeIntervaltypealiasfor Double뿐이 므로 쉽게 변환 할 수 Int있습니다. 시간은 밀리 초 미만의 정밀도로 초 단위로 측정됩니다.
  • DateInterval실제 시작 및 종료 시간이 포함 된을 참조하십시오 .
  • 1970 년 이후의 시간을 사용하는 것은 Java 타임 스탬프 와 유사합니다 .

이 기능을 복사하여 붙여 넣기 만하면됩니다. 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
}

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

이것은 내가 생각해 낸 스 니펫이며 Swift 4가 설치된 Macbook에서 작동하는 것 같습니다.

다른 시스템에서는 테스트하지 않았지만 어쨌든 공유 할 가치가 있다고 생각했습니다.

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 elapsed ms : 1402.415125 나노를 사용하려는 경우 "useNanos"매개 변수가 있습니다. 필요에 따라 자유롭게 변경하십시오.

   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 10.1 (10B61)
  • 스위프트 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)")

참고 URL : https://stackoverflow.com/questions/24755558/measure-elapsed-time-in-swift

반응형