Measure elapsed time in Swift
Asked Answered
M

20

189

How can we measure the time elapsed for running a function in Swift? I am trying to display the elapsed time like this: "Elapsed time is .05 seconds". Saw that in Java, we can use System.nanoTime(), are there any equivalent methods available in Swift to accomplish this?

Please have a look at the sample program:

func isPrime(_ 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) {
    print("Prime number")
} else {
    print("NOT a prime number")
}
Manson answered 15/7, 2014 at 10:14 Comment(6)
it is unrelated to your time-measuring issue, but the loop can be stopped at sqrt(number) instead of number, and you can save a little more time – but there are much more ideas optimising seeking primes.Cavorelievo
@Cavorelievo Please ignore the algorithm used. I am trying to figure out how we can measure the elapsed time?Manson
you can use NSDate objects and you can measure the difference between them.Cavorelievo
If you are using XCode, I recommend you use the new performance testing feature. It does all the heavy lifting for you and even runs it multiple times and gives you the average time with its standard deviation...Quadrinomial
@Quadrinomial Although perhaps not an answer I'd love to see your comment on Xcode's performance testing feature expanded. Where can I learn more? How can I use it to compare the time taken by various methods contributing to a run?Occupational
@Occupational You can measure performance of whole blocks directly in unit tests. For example, see this. If you want to break a run down further(say, line by line code), check out Time Profiler which is part of Instruments. This is much more powerful and comprehensive.Quadrinomial
R
330

Update

With Swift 5.7, everything below becomes obsolete. Swift 5.7 introduces the concept of a Clock which has a function designed to do exactly what is required here.

There are two concrete examples of a Clock provided: ContinuousClock and SuspendingClock. The former keeps ticking when the system is suspending and the latter does not.

The following is an example of what to do in Swift 5.7

func doSomething()
{
    for i in 0 ..< 1000000
    {
        if (i % 10000 == 0)
        {
            print(i)
        }
    }
}

let clock = ContinuousClock()

let result = clock.measure(doSomething)

print(result) // On my laptop, prints "0.552065882 seconds"

It also allows you to measure closures directly, of course

let clock = ContinuousClock()

let result = clock.measure {
    for i in 0 ..< 1000000
    {
        if (i % 10000 == 0)
        {
            print(i)
        }
    }
}

print(result) // "0.534663798 seconds"

Pre Swift 5.7

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

Rame answered 15/7, 2014 at 10:35 Comment(18)
This appears to give results good up to around +/-2microsec. Maybe that varies by platform, however.Stipendiary
Using calendar time for measuring execution time is really ill-advised. There's no guarantee your thread has been active on the CPU (i.e. it could have spend most of its time waiting to be scheduled on the CPU)Fag
@AlexanderMomchliov Sorry, but it works well enough. There will only be a problem if the the thread gets pre-emptively switched out which is unlikely on a modern multicore CPU.Rame
@AlexanderMomchliov Another point is of course that the question asks for elapsed time - not time spent actually running on the CPU.Rame
swift3 doesn't work for me, it returns 0.0114653027 which i know for a fact that is not true, the one with dates returns 4.77720803022385secStarofbethlehem
@CristiBăluță Any chance you could do some debugging to see where it goes wrong? I can't see an obvious error in the code.Rame
I've fixed a bug: It was 1_000_0000_000 instead of 1_000_000_000. (See the block with four 0s?)Godspeed
Unfortunately not. The uptime is completely out for some reason. I have a test which says took 1004959766 nanoseconds (about a second), but was definitely running for about 40 seconds. I used Date alongside, and sure enough that reports 41.87 seconds. I don't know what uptimeNanoseconds is doing, but it's not reporting the correct duration.Horrific
@Horrific I just ran the code with the following closure { Thread.sleep(forTimeInterval: 40.0) return 2 } and it gives the right answer (just over 40 seconds)Rame
@Rame but that's for the Thread class, not DispatchTime. The former takes a TimeInterval in seconds. uptimeNanoseconds is a UInt64.Horrific
@Horrific Yes. That was to make the closure take 40 seconds to run. Using that closure as problemBlock and the timing code in my Swift 3 answer, I get almost exactly 40 seconds reported. My code gives the right answer.Rame
Is there advantage to your solution vs the solution below (written by Klass)?Titlark
@Honey There are two reasons why I prefer my solution over that of Klass: 1. It doesn't use core foundation, which, I think, might get deprecated. The DispatchTime version is probably "the future". 2. In order to use it, I just pass the block of code to a function call. Klass's solution requires you to annotate the code to be timed with a before and after step. Not saying mine is better or worse but I think using it is a bit cleaner.Rame
What is "checkanswer"?Jos
@Jos it compared the return value from problemBlock against the correct answer and printed a nasty message if they differed (if they were different, it meant the code had regressed). What it does is totally irrelevant to the point of this SO question.Rame
Sorry for the big edit, prioritizing your newer solution, but I would even recommend to delete your NSDate code entirely: it's totally unreliable to use NSDate: NSDate is based on system clock, which can change at any time due to many different reasons, such as network time sync (NTP) updating the clock (happens often to adjust for drift), DST adjustments, leap seconds, and user manual adjustment of the clock. Additionally, you can't publish anymore on the AppStore any app using Swift 1: Swift 3 is the bare minimum. So there is no point to keep your NSDate code other than to say "don't do it".Pecos
@Cœur your criticism of the NSDate version is valid (except DST changes won't affect NSDate which is always in GMT), and your edit to reverse the order is definitely an improvement.Rame
Note that the time that DispatchTime returns is not incremented when the device is sleepingEgotism
W
87

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

print("The task took \(timer.stop()) seconds.")
Wingover answered 26/10, 2014 at 21:30 Comment(6)
"Repeated calls to this function do not guarantee monotonically increasing results." according to its documentation.Henshaw
More context: "Repeated calls to this function do not guarantee monotonically increasing results. The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock."Wingover
Developer should take "synchronization with external time references" and "an explicit user change of the clock" into account. Did you mean that it's impossible for the two situation to happen?Henshaw
@FranklinYu no, I just wanted to state these are the two possible reasons. If you rely on always getting monotonically increasing values there are other options.Wingover
sometimes I wish to measure a short period of time; if synchronization happens during that, I may end up with negative time. There is actually mach_absolute_time which does not suffer from this problem, so I am wondering why that one is not widely known. Maybe just because it is not well documented.Henshaw
This is a cool name with somehow related semantics. Thanks.Vacla
H
75

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.

Henshaw answered 26/5, 2016 at 2:44 Comment(5)
the best answer - but what, the hell, is the best approach to take?!Hoard
Option 5 suited best to me. If you considering multi-platform code try this one. Both Darwin and Glibc libraries have clock() function.Khmer
For the System Clock based solutions: I had success with ProcessInfo.processInfo.systemUptime, mach_absolute_time(), DispatchTime.now() and CACurrentMediaTime(). But the solution with clock() was not converting to seconds properly. So I would advise to update your first sentence to: "Use ProcessInfo.systemUptime or DispatchTime for accurate start-up time."Pecos
Great answer. Explained everything.Counsellor
Great answer. If I may, option 2 could be optimised using clock_gettime_nsec_np(CLOCK_UPTIME_RAW) which does the time base conversion for you and provides nanosec value (see manpagez.com/man/3/clock_gettime_nsec_np)Malignant
R
60

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
Repeater answered 7/2, 2018 at 22:28 Comment(4)
Can someone explain why * -1?Celina
@MaksimKniazev Because it's negative value, people want positive one!Khufu
@Khufu I had no idea "startingPoint.timeIntervalSinceNow" produces negative value ...Celina
@MaksimKniazev Apple says that: If the date object is earlier than the current date and time, this property’s value is negative.Mcdowell
S
20

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
}
Subaquatic answered 30/5, 2019 at 16:16 Comment(1)
I tried to do something like this but it ends up into arithmetic overflow error!Nomarch
H
18
let start = NSDate()

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

let elapsed = start.timeIntervalSinceNow

// elapsed is a negative value.
Hovel answered 22/6, 2015 at 9:3 Comment(2)
abs(start.timeIntervalSinceNow)//<--positive valueSoutache
pretty much the most straight forward solution I've seen, thank youTaker
S
7

It looks like iOS 13 introduced a new API to use with DispatchTime that removes a need to calculate the difference between two timestamps manually.

distance(to:)

let start: DispatchTime = .now()
heavyTaskToMeasure()
let duration = start.distance(to: .now())
print(duration) 
// prints: nanoseconds(NUMBER_OF_NANOSECONDS_BETWEEN_TWO_TIMESTAMPS)

Sadly the documentation is not provided, but after doing some tests it looks like the .nanoseconds case is always returned.

With a simple extension you could convert the DispatchTimeInterval to TimeInterval. credit

extension TimeInterval {
    init?(dispatchTimeInterval: DispatchTimeInterval) {
        switch dispatchTimeInterval {
        case .seconds(let value):
            self = Double(value)
        case .milliseconds(let value):
            self = Double(value) / 1_000
        case .microseconds(let value):
            self = Double(value) / 1_000_000
        case .nanoseconds(let value):
            self = Double(value) / 1_000_000_000
        case .never:
            return nil
        }
    }
}
Stickleback answered 16/11, 2021 at 12:20 Comment(0)
D
6

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

Deaf answered 16/1, 2015 at 18:34 Comment(1)
"Repeated calls to this function do not guarantee monotonically increasing results." according to documentation.Henshaw
S
3

Simple helper function for measuring execution time with closure.

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

Usage:

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

Result: #Init - execution took 1.00104497105349 seconds

Sewn answered 13/12, 2017 at 17:7 Comment(0)
C
2

you can measure the nanoseconds like e.g. this:

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)")
Cavorelievo answered 15/7, 2014 at 10:35 Comment(6)
I just ran this with no code placed in the "// your long procedure", and it gave a result of 240900993, which is 0.24 seconds.Stipendiary
instantiating an NSCalendar is expensive procedure, but you can do it before you start to run your procedure, so it won't be added to your long procedure's runtime... bear in mind calling creating an NSDate instance and calling –components(_:, fromDate:) method still takes time, so probably you will never get 0.0 nanoseconds.Cavorelievo
From the looks of the NSCalendar constructor, it appears that it may not be possible to create it ahead of time (startDate is a required input). It's kind of surprising that it seems to be such a hog.Stipendiary
you may do this (my updated answer), the measured time without any additional procedure is about 6.9 microseconds on my device.Cavorelievo
Cool, thanks. The basic procedure is now similar to JeremyP's answer, but the reporting is different.Stipendiary
If you multiply timeInterval in my answer by 1000000000 you'll get the answer in nanoseconds.Rame
P
2

I use this:

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

I don't know if it's more or less accurate than previously posted. But the seconds have a lot of decimals and seem to catch small code changes in algorithms like QuickSort using swap() vs. implementing swap urself etc.

Remember to crank up your build optimizations when testing performance:

Swift compiler optimizations

Presocratic answered 19/6, 2015 at 19:33 Comment(0)
D
2

Here is my try for the simplest answer:

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

Notes

  • startTime and endTime are of the type TimeInterval, which is just a typealias for Double, so it is easy to convert it to an Int or whatever. Time is measured in seconds with sub-millisecond precision.
  • See also DateInterval, which includes an actual start and end time.
  • Using the time since 1970 is similar to Java timestamps.
Deputy answered 6/12, 2017 at 5:12 Comment(1)
easiest way, would like it in two strings, second is let elapsedTime = Date().timeIntervalSince1970 - startTimeInsoluble
S
2

The recommend way to check elapsed time/performance is using the measure function that is available in XCTests.

It isn't reliable to write your own measure blocks, since the performance (and therefore execution/elapsed time) of a block of code is influenced by e.g. CPU caches.

The second time a function is invoked, would likely be quicker than the first time it is invoked, although it can vary a few %. Therefore 'benchmarking' by using your own closures (given all over the place here) by executing it once, can give different results than your code being executed in production by real users.

The measure function invokes your block of code several times, mimicking the performance/elapsed time of your code like it is used in production (at least gives more accurate results).

Shrewd answered 23/1, 2021 at 15:26 Comment(0)
C
1

Swift 5+

I have borrowed the idea from Klaas to create a lightweight struct to measure running and interval time:

Code Usage:

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

The stop function does not have to be called, as the print function will give the time lapsed. It may be called repeatedly to get the time lapsed. But to stop the timer at certain point in the code use timer.stop() it may also be used to return the time in seconds: let seconds = timer.stop() After the timer is stopped the interval timer will not, so the print("Running: \(timer) ") will give the correct time even after a few lines of code.

Following is the code for RunningTimer. It is tested for 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"}
    }
}
Clementina answered 16/12, 2015 at 22:0 Comment(0)
D
1

Wrap it up in a completion block for easy use.

public class func secElapsed(completion: () -> Void) {
    let startDate: NSDate = NSDate()
    completion()
    let endDate: NSDate = NSDate()
    let timeInterval: Double = endDate.timeIntervalSinceDate(startDate)
    println("seconds: \(timeInterval)")
}
Deathblow answered 8/3, 2016 at 6:37 Comment(0)
G
1

Static Swift3 class for basic function timing. It will keep track of each timer by name. Call it like this at the point you want to start measuring:

Stopwatch.start(name: "PhotoCapture")

Call this to capture and print the time elapsed:

Stopwatch.timeElapsed(name: "PhotoCapture")

This is the output: *** PhotoCapture elapsed ms: 1402.415125 There is a "useNanos" parameter if you want to use nanos. Please feel free to change as needed.

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

}

Greenleaf answered 17/5, 2018 at 16:35 Comment(1)
Your whole work with mach_timebase_info is already implemented better than you did in the source code of ProcessInfo.processInfo.systemUptime. So simply do watches[name] = ProcessInfo.processInfo.systemUptime. And * TimeInterval(NSEC_PER_MSEC) if you want nanos.Pecos
S
1

From Swift 5.7 (macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0), you can use ContinuousClock and the measure block, which returns a Duration object. It has components that contain the measured time in seconds or attoseconds, which is 1×10−18 of a second.

let clock = ContinuousClock()

let duration = clock.measure {
    // put here what you want to measure
}

print("Duration: \(duration.components.seconds) seconds")
print("Duration: \(duration.components.attoseconds) attoseconds")
Sill answered 4/1, 2023 at 14:52 Comment(0)
C
0

This is the snippet I came up with and it seems to work for me on my Macbook with Swift 4.

Never tested on other systems, but I thought it's worth sharing anyway.

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

Here's an example of how to use it:

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

Another way is to do it more explicitly:

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")
Crucifix answered 25/3, 2018 at 12:32 Comment(0)
H
0

This is how I wrote it.

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

To measure a algorithm use it like that.

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

print("Block is running \(time) seconds.")
Highpowered answered 2/4, 2018 at 23:45 Comment(1)
How accurate is that time? Because I used timer before to update every 0.1 seconds. I compared this answer and one with timer and result from this answer is 0.1 seconds is more then with timer..Celina
T
0

Based on Franklin Yu answer and Cœur comments

Details

  • Xcode 10.1 (10B61)
  • Swift 4.2

Solution 1

measure(_:)

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

Usage of solution 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)")
Ticktack answered 26/6, 2018 at 14:32 Comment(3)
Not sure why we would need to use variable implementations. Using Date to measure anything is extremely discouraged (but systemUptime or clock() are OK). As for tests, we have measure(_:) already.Pecos
Another note: why making the closure optional?Pecos
@Cœur you right! Thanks for your comments. I will update post later.Ticktack

© 2022 - 2024 — McMap. All rights reserved.