问题
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(), is there any equivalent methods are available in Swift to accomplish this?
Please have a look at the sample program:
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\");
}
回答1:
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.".
回答2:
This is a handy timer class based on CoreFoundation
s 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.")
回答3:
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:
- ProcessInfo.systemUptime.
- mach_absolute_time with
mach_timebase_info
as mentioned in this answer. clock()
in POSIX standard.times()
in POSIX standard. (Too complicated since we need to consider user-time v.s. system-time, and child processes are involved.)- DispatchTime (a wrapper around Mach time API) as mentioned by JeremyP in accepted answer.
- CACurrentMediaTime().
Wall Clock based:
(never use those for metrics: see below why)
NSDate
/Date
as mentioned by others.CFAbsoluteTime
as mentioned by others.- DispatchWallTime.
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.
回答4:
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!
回答5:
let start = NSDate()
for index in 1...10000 {
// do nothing
}
let elapsed = start.timeIntervalSinceNow
// elapsed is a negative value.
回答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
回答7:
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
回答8:
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)")
回答9:
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:
回答10:
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
andendTime
are of the type TimeInterval, which is just atypealias
forDouble
, so it is easy to convert it to anInt
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.
回答11:
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
}
回答12:
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"}
}
}
回答13:
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)")
}
回答14:
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")
回答15:
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.")
回答16:
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))")
}
}
}
回答17:
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)")
来源:https://stackoverflow.com/questions/24755558/measure-elapsed-time-in-swift