快速测量已用时间
我们如何测量在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 }
这是基于CoreFoundation
的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 } } }
你可以像这样使用它:
let timer = ParkBenchTimer() // ... a long runnig task ... println("The task took \(timer.stop()) seconds.")
使用clock
, ProcessInfo.systemUptime
或DispatchTime
来简化启动时间。
据我所知,至less有七种测量时间的方法:
-
NSDate
被别人所提及。 - 其他人提到的
CFAbsoluteTime
。 -
ProcessInfo.systemUptime
。 -
mach_absolute_time
与mach_timebase_info
正如本答案所述 。 -
clock()
在POSIX标准 。 -
times()
在POSIX标准中 。 (太复杂了,因为我们需要考虑用户时间和系统时间,并涉及subprocess。) -
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优化:
我借鉴了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)") }