Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Measure elapsed time in Swift

Here's a Swift function I wrote to measure Project Euler problems in Swift

As of Swift 3, there is now a version of Grand Central Dispatch that is "swiftified". So the correct answer is probably to use the DispatchTime API.

My function would look something like:

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

Old answer

For Swift 1 and 2, my function uses 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
}

Note that using NSdate for timing functions is discouraged: "The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock.".


This is a handy timer class based on 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
        }
    }
}

You can use it like this:

let timer = ParkBenchTimer()

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

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

Use clock, ProcessInfo.systemUptime, or DispatchTime for simple start-up time.


There are, as far as I know, at least ten ways to measure elapsed time:

Monotonic Clock based:

  1. ProcessInfo.systemUptime.
  2. mach_absolute_time with mach_timebase_info as mentioned in this answer.
  3. clock() in POSIX standard.
  4. times() in POSIX standard. (Too complicated since we need to consider user-time v.s. system-time, and child processes are involved.)
  5. DispatchTime (a wrapper around Mach time API) as mentioned by JeremyP in accepted answer.
  6. CACurrentMediaTime().

Wall Clock based:

(never use those for metrics: see below why)

  1. NSDate/Date as mentioned by others.
  2. CFAbsoluteTime as mentioned by others.
  3. DispatchWallTime.
  4. gettimeofday() in POSIX standard.

Option 1, 2 and 3 are elaborated below.

Option 1: Process Info API in Foundation

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

where diff:NSTimeInterval is the elapsed time by seconds.

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

where diff:Double is the elapsed time by nano-seconds.

Option 3: POSIX clock API

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

where diff:Double is the elapsed time by seconds.

Why Not Wall-Clock Time for Elapsed Time?

In documentation of CFAbsoluteTimeGetCurrent:

Repeated calls to this function do not guarantee monotonically increasing results.

Reason is similar to currentTimeMillis vs nanoTime in Java:

You can't use the one for the other purpose. The reason is that no computer's clock is perfect; it always drifts and occasionally needs to be corrected. This correction might either happen manually, or in the case of most machines, there's a process that runs and continually issues small corrections to the system clock ("wall clock"). These tend to happen often. Another such correction happens whenever there is a leap second.

Here CFAbsoluteTime provides wall clock time instead of start-up time. NSDate is wall clock time as well.


Swift 4 shortest answer:

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

It will print you something like 1.02107906341553 seconds elapsed (time of course will vary depending on the task, I'm just showing this for you guys to see the decimal precision level for this measurement).

Hope it helps someone in Swift 4 from now on!

Update

If you want to have a generic way of testing portions of code, I'd suggest the next snippet:

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

let start = NSDate()

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

let elapsed = start.timeIntervalSinceNow

// elapsed is a negative value.

Just Copy and Paste this function. Written in swift 5. Copying JeremyP here.

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

Use it like

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

You could create a time function for measuring you calls. I am inspired by Klaas' answer.

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

This function would allow you to call it like this time (isPrime(7)) which would return a tuple containing the result and a string description of the elapsed time.

If you only wish the elapsed time you can do this time (isPrime(7)).duration