快速测量已用时间

我们如何测量在Swift中运行函数的时间? 我正在尝试显示这样的经过时间:“Elasped time是0.05秒”。 看到在Java中 ,我们可以使用System.nanoTime(),是否有任何等价的方法在Swift中可用来实现这一点?

请看一下示例程序:

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

这里是我写的一个Swift函数,用来衡量Swift中的Project Euler问题

它使用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 } 

更新

从Swift 3开始,现在有一个版本的Grand Central Dispatch“swifitfied”。 所以新的正确答案可能是使用DispatchTime API 。

然后我的function看起来像(没有实际testing或任何东西):

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

这是基于CoreFoundationCFAbsoluteTime的便捷计时器类:

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

你可以像这样使用它:

 let timer = ParkBenchTimer() // ... a long runnig task ... println("The task took \(timer.stop()) seconds.") 

使用clockProcessInfo.systemUptimeDispatchTime来简化启动时间。


据我所知,至less有七种测量时间的方法:

  1. NSDate被别人所提及。
  2. 其他人提到的CFAbsoluteTime
  3. ProcessInfo.systemUptime
  4. mach_absolute_timemach_timebase_info正如本答案所述 。
  5. clock()在POSIX标准 。
  6. times()在POSIX标准中 。 (太复杂了,因为我们需要考虑用户时间和系统时间,并涉及subprocess。)
  7. DispatchTime (马赫时间API的一个包装)正如JeremyP在接受的答案中所提到的。

选项3,4和5详细说明如下。

选项3:Foundation中的Process Info API

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

其中diff:NSTimeInterval是以秒为单位的经过时间。

选项4: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) } 

其中diff:Double是经过了纳秒的时间。

选项5:POSIX时钟API

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

diff:Double是以秒为单位的经过时间。

为什么不是挂钟时间?

CFAbsoluteTimeGetCurrent文档中:

重复调用此函数并不能保证单调递增的结果。

原因与Java中的currentTimeMillis vs nanoTime类似:

您不能将其用于其他目的。 原因是没有电脑的时钟是完美的; 它总是漂移,偶尔需要纠正。 这种修正可能是手动发生的,或者在大多数机器的情况下,有一个运行的过程,并不断地对系统时钟(“挂钟”)进行小的修正。 这些往往是经常发生的。 另一个这样的纠正发生在闰秒。

这里CFAbsoluteTime提供挂钟时间而不是启动时间。 NSDate也可能是挂钟时间。

 let start = NSDate() for index in 1...10000 { // do nothing } let elapsed = start.timeIntervalSinceNow // elapsed is a negative value. 

你可以创build一个time函数来测量你的电话。 我受到克拉斯的回答的启发。

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

这个函数可以让你像这样调用它time (isPrime(7)) ,它将返回一个包含结果的元组和一个经过时间的string描述。

如果你只希望经过这段time (isPrime(7)).duration

你可以像这样测量纳秒

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

我使用这个:

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

我不知道它是否比以前发布的更准确或更不准确。 但秒有很多小数,似乎赶上像QuickSort的algorithm使用swap()与实现交换自己等小代码更改等。

请记住在testing性能时加快构build优化:

Swift编译器优化

我借鉴了Klaas的想法来创build一个轻量级结构来测量运行和间隔时间:

代码用法:

 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 

停止function不需要被调用,因为打印function会使时间stream逝。 它可能会被重复调用来获取时间。 但是在代码中使用timer.stop()来停止定时器的某个时刻,它也可能用于以秒为单位返回时间: let seconds = timer.stop()定时器停止后,间隔定时器不会,所以print("Running: \(timer) ")会在几行代码后给出正确的时间。

以下是RunningTimer的代码。 它是testing的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"} } } 

把它包装在一个完成块中,以方便使用。

 public class func secElapsed(completion: () -> Void) { let startDate: NSDate = NSDate() completion() let endDate: NSDate = NSDate() let timeInterval: Double = endDate.timeIntervalSinceDate(startDate) println("seconds: \(timeInterval)") }