Measure Elapsed Time in Swift

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

How to properly measure elapsed time in background in Swift

Ok, like I mention in the edit 2 of the question:
The first issue was because "seconds" is a Int and then it almost always gains or lose when converting it from Double.

But the main problem was that i had to invalidate the timer when the app enter in background and i didn't.

So now with invalidating the timer when the app gets the notification that will enter background and then starting it when it enter foreground everything works fine.

getTimeElapsed in Swift with NSTimer

Few small steps:

  1. Define the start time: (should happen at the same time you start the timer)

    startTime = (NSDate.timeIntervalSinceReferenceDate())

  2. Measure the time difference

    let elapsed = NSDate.timeIntervalSinceReferenceDate() - startTime

Proper way to measure time in swift

A couple of observations:

  1. In terms of the best way to measure speed you can use Date or CFAbsoluteTimeGetCurrent, but you'll see that the documentation for those will warn you that

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

    This is effectively warning you that, in the unlikely event that there is an adjustment to the system's clock in the intervening period, the calculated elapsed time may not be entirely accurate.

    It is advised to use mach_time if you need a great deal of accuracy when measuring the elapsed time. This involves some annoying CPU-specific adjustments (See Technical Q&A 1398.), but CACurrentMediaTime offers a simple alternative because it uses mach_time (which does not suffer this problem), but converts it to seconds to make it really easy to use.

  2. The aforementioned notwithstanding, it seems that there is a more fundamental issue at play here: It looks like you're trying to reconcile a difference between to very different ways of running Swift code, namely:

    swiftc hello.swift -o hello
    time ./hello

    and

    time swift hello.swift

    The former is compiling hello.swift into a stand alone executable. The latter is loading swift REPL, which then effectively interprets the Swift code.

    This has nothing to do with the "proper" way to measure time. The time to execute the pre-compiled version should always be faster than invoking swift and passing it a source file. Not only is there more more overhead in invoking the latter, but the execution of the pre-compiled version is likely to be faster once execution starts, as well.

  3. If you're really benchmarking the performance of running these routines, you should not rely on a single sort of 5000 items. I'd suggest sorting millions of items and repeating this multiple times and averaging the statistics. A single iteration of the sort is unsufficient to draw any meaningful conclusions.

Bottom line, you need to decide whether you want to benchmark just the execution of the code, but also the overhead of starting the REPL, too.

Swift - Calculating elapsed time takes too long?

For performance reasons, you should pull the instantiation of the date formatter out of the method, because that's notoriously computationally intensive.

I'd also suggest using a DateComponentsFormatter to simplify the formatting of the elapsed time.

So, define your two formatters:

let dateFormatter: DateFormatter = {
let _formatter = DateFormatter()
_formatter.dateFormat = "yyyy-MM-dd' 'HH:mm:ss"
_formatter.locale = Locale(identifier: "en_US_POSIX")
_formatter.timeZone = TimeZone(abbreviation: "AST") // Curious; we usually use `TimeZone(secondsFromGMT: 0)` (i.e. GMT/UTC/Zulu)
return _formatter
}()

let componentsFormatter: DateComponentsFormatter = {
let _formatter = DateComponentsFormatter()
_formatter.maximumUnitCount = 1
_formatter.unitsStyle = .abbreviated
return _formatter
}()

And then your function is considerably simplified:

func dateDiff(_ string: String) -> String? {
guard let date = dateFormatter.date(from: string) else { return nil }

return componentsFormatter.string(from: date, to: Date())
}

Also note that:

  • I used TimeZone directly, rather round-tripping through NSTimeZone;
  • I set the locale to be en_US_POSIX, which you should always use if the source of the date strings is a web service or database;
  • I eliminated the conversion of “now” to a string and back; just use Date() directly;

The only other thing that looks suspicious is the use of AST for the timezone. Usually date strings are saved in GMT/UTC/Zulu (e.g., RFC 3339 or ISO 8601). If you have control over that, that's probably best practice, avoiding problems if users change time zones;

How to log a method's execution time exactly in milliseconds?

NSDate *methodStart = [NSDate date];

/* ... Do whatever you need to do ... */

NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

Swift:

let methodStart = NSDate()

/* ... Do whatever you need to do ... */

let methodFinish = NSDate()
let executionTime = methodFinish.timeIntervalSinceDate(methodStart)
print("Execution time: \(executionTime)")

Swift3:

let methodStart = Date()

/* ... Do whatever you need to do ... */

let methodFinish = Date()
let executionTime = methodFinish.timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")

Easy to use and has sub-millisecond precision.



Related Topics



Leave a reply



Submit