测量Swift中的经过时间

时间:2014-07-15 10:14:32

标签: swift time nsdate elapsedtime

我们如何衡量在Swift中运行函数所用的时间?我试图像这样显示经过的时间:“经过的时间是.05秒”。看到in 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");
}

19 个答案:

答案 0 :(得分:184)

这是我编写的Swift函数,用于测量Swift中的Project Euler个问题

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

我的功能看起来像是:

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

旧答案

对于Swift 1和2,我的函数使用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
}

请注意,不建议使用NSdate作为计时功能:“由于与外部时间参考同步或由于时钟的显式用户更改,系统时间可能会减少。”。< /子>

答案 1 :(得分:63)

这是一个基于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
        }
    }
}

你可以像这样使用它:

let timer = ParkBenchTimer()

// ... a long runnig task ...

println("The task took \(timer.stop()) seconds.")

答案 2 :(得分:41)

使用clockProcessInfo.systemUptimeDispatchTime来缩短启动时间。

据我所知,至少有十种方法可以衡量经过的时间:

基于单调时钟:

  1. ProcessInfo.systemUptime
  2. {li> mach_absolute_timethis answer中提到的mach_timebase_info。 {li> clock()POSIX standard。 在POSIX standard
  3. times()。 (因为我们需要,太复杂了 考虑用户时间vs.系统时间和子进程是 参与。)
  4. DispatchTime(围绕马赫时间API的包装),正如JeremyP在接受的答案中提到的那样。
  5. CACurrentMediaTime()
  6. 基于挂钟:

    (永远不要将这些用于指标:请参阅下面的原因)

      其他人提到的
    1. NSDate / Date
    2. CFAbsoluteTime正如其他人所说。
    3. DispatchWallTime
    4. {li> gettimeofday()POSIX standard

      下面详细说明了选项1,2和3。

      选项1:Foundation中的Process Info API

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

      其中diff:NSTimeInterval是经过的秒数。

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

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

      选项3:POSIX时钟API

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

      其中diff:Double是经过的秒数。

      为什么不是经过时间的闹钟时间?

      CFAbsoluteTimeGetCurrent的文档中:

        

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

      原因类似于currentTimeMillis vs nanoTime in Java

        

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

      此处CFAbsoluteTime提供挂钟时间而非启动时间 时间。 NSDate也是挂钟时间。

答案 3 :(得分:22)

斯威夫特4最短的答案:

let startingPoint = Date()
//  ... intensive task
print("\(startingPoint.timeIntervalSinceNow * -1) seconds elapsed")

它会打印出类似 1.02107906341553秒已过的(时间当然会因任务而异,我只是向你们展示这个测量的小数精度等级)。

希望从现在开始帮助Swift 4中的某个人!

答案 4 :(得分:13)

let start = NSDate()

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

let elapsed = start.timeIntervalSinceNow

// elapsed is a negative value.

答案 5 :(得分:6)

您可以创建一个time功能来衡量您的来电。 我受Klaas'回答的启发。

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))一样调用它,它会返回一个包含结果的元组和一个经过时间的字符串描述。

如果您只希望经过时间,可以执行此操作time (isPrime(7)).duration

答案 6 :(得分:3)

简单的辅助函数,用于通过闭包来测量执行时间。

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

<强>用法:

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

<强>结果: #Init - execution took 1.00104497105349 seconds

答案 7 :(得分:2)

您可以测量纳秒,例如这样:

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

答案 8 :(得分:2)

这是我尝试最简单的答案:

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

备注

  • startTimeendTime属于TimeInterval类型,typealias只是Double,因此很容易将其转换为{ {1}}或其他什么。时间以秒为单位测量,精确度为亚毫秒级。
  • 另请参阅DateInterval,其中包括实际的开始和结束时间。
  • 使用自1970年以来的时间与Java timestamps类似。

答案 9 :(得分:2)

我用这个:

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

我不知道它是否比之前发布的更准确或更准确。但是秒数有很多小数,并且似乎在使用swap()与实现交换等等的算法中捕获QuickSort算法中的小代码更改。

在测试性能时,请记住提高构建优化:

Swift compiler optimizations

答案 10 :(得分:1)

将其包裹在完成块中以方便使用。

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

答案 11 :(得分:1)

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

代码使用:

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

不必调用停止功能,因为打印功能会给出时间。可以反复调用它以使时间流逝。 但是要在代码中的某个点停止计时器使用timer.stop(),它也可以用来以秒为单位返回时间:let seconds = timer.stop() 定时器停止后,间隔定时器不会,所以即使经过几行代码,print("Running: \(timer) ")也会给出正确的时间。

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

答案 12 :(得分:0)

这是我提出的片段,它似乎适用于我的Macbook和Swift 4。

从未在其他系统上测试过,但我认为无论如何都值得分享。

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

以下是如何使用它的示例:

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

另一种方法是更明确地做到这一点:

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

答案 13 :(得分:0)

这就是我写它的方式。

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

要测量算法使用,就像那样。

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

print("Block is running \(time) seconds.")

答案 14 :(得分:0)

静态Swift3类用于基本功能定时。它将按名称跟踪每个计时器。在您想要开始测量时,请将其称为:

 import Foo from './Foo'

 export default Foo.extend({ [extended component definition] })

调用此方法捕获并打印已用时间:

Stopwatch.start(name: "PhotoCapture")

这是输出:*** PhotoCapture经过ms:1402.415125 如果您想使用nanos,则有一个“useNanos”参数。 请随意根据需要进行更改。

Stopwatch.timeElapsed(name: "PhotoCapture")

}

答案 15 :(得分:0)

基于Franklin Yu的答案和Cœur的评论

详细信息

  • Xcode 10.1(10B61)
  • 迅速4.2

解决方案1 ​​

measure(_:)

解决方案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)
    }
}

解决方案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)")

答案 16 :(得分:0)

只需复制并粘贴此功能。速写5。 在此处复制JeremyP。

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

使用方式

calculateTime {
     exampleFunc()// function whose execution time to be calculated
}

答案 17 :(得分:0)

The recommend way 使用 measure 秒内可用的 XCTest 函数来检查经过的时间/性能。

编写自己的度量块是不可靠的,因为代码块的性能(以及因此执行/经过的时间)受例如影响。 CPU 缓存。

函数第二次被调用,可能比第一次被调用更快,although it can vary a few %。因此,通过使用您自己的闭包(此处随处可见)进行“基准测试”,执行一次,可以得到与实际用户在生产环境中执行的代码不同的结果。

measure 函数调用您的代码块多次,模拟您的代码的性能/运行时间,就像它在生产中使用一样 (at least gives more accurate results)。

答案 18 :(得分:0)

类似于 C# 的 Stopwatch 类的解决方案。

用法:

let s = Stopwatch()
s.Start()
    
for _ in 1..<5 {
    Thread.sleep(forTimeInterval: 5)
    print("Sleeped \(s.ElapsedS) S")
}
    
s.Stop()
print("Sleeped \(s.ElapsedS) S")

结果:

Sleeped 5.001374235 S
Sleeped 10.002803108 S
Sleeped 15.003942841 S
Sleeped 20.004250347000003 S
Sleeped 20.004299962 S

代码:

public class Stopwatch {
    public var ElapsedS : Double {
        return ElapsedMs/1000
    }
    public var ElapsedMs: Double {
        guard let StartTime = StartTime else { return 0 }
        
        if IsGoing {
            let end = DispatchTime.now()
            let nanoTime = end.uptimeNanoseconds - StartTime.uptimeNanoseconds
            
            return Double(nanoTime) / 1000000
        }
        
        if let EndTime = EndTime {
            let nanoTime = EndTime.uptimeNanoseconds - StartTime.uptimeNanoseconds
            return Double(nanoTime) / 1000000
        }
        
        return 0
    }
    
    private var StartTime : DispatchTime? = nil
    private var EndTime : DispatchTime? = nil
    private var IsGoing = false
    
    public func Start() {
        IsGoing = true
        StartTime = DispatchTime.now()
    }
    
    public func Stop() {
        EndTime = DispatchTime.now()
        IsGoing = false
    }
}