﻿ Measure Elapsed Time in Swift - ITCodar

# 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 3func 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}``

For Swift 1 and 2, my function uses NSDate:

``// Swift 1func 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 hellotime ./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.

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