如何对Swift代码执行进行基准测试?

时间:2014-07-29 00:03:10

标签: swift performance

是否有一种方法/软件可以提供执行用Swift编写的代码块所需的精确时间,而不是以下内容?

let date_start = NSDate()

// Code to be executed 

println("\(-date_start.timeIntervalSinceNow)")

7 个答案:

答案 0 :(得分:101)

如果您只想要一个代码块的独立计时功能,我使用以下Swift辅助函数:

func printTimeElapsedWhenRunningCode(title:String, operation:()->()) {
    let startTime = CFAbsoluteTimeGetCurrent()
    operation()
    let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
    print("Time elapsed for \(title): \(timeElapsed) s.")
}

func timeElapsedInSecondsWhenRunningCode(operation: ()->()) -> Double {
    let startTime = CFAbsoluteTimeGetCurrent()
    operation()
    let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
    return Double(timeElapsed)
}

前者将注销给定代码段所需的时间,后者将其作为float返回。作为第一个变体的一个例子:

printTimeElapsedWhenRunningCode(title:"map()") {
    let resultArray1 = randoms.map { pow(sin(CGFloat($0)), 10.0) }
}

将注销以下内容:

  

map()的时间已过去:0.0617449879646301 s

请注意Swift benchmarks will vary heavily based on the level of optimization you select,因此这可能仅对Swift执行时间的相对比较有用。即使这可能会在每个beta版本的基础上发生变化。

答案 1 :(得分:31)

如果您希望深入了解某个代码块的性能并确保在进行编辑时性能不会受到影响,那么最好的方法是使用XCTest's测量性能函数,例如measure(_ block: () -> Void)

编写一个单元测试,执行您想要进行基准测试的方法,该单元测试将多次运行,为您提供所需时间和结果偏差

func testExample() {

    self.measure {
        //do something you want to measure
    }
}

您可以在Testing with Xcode -> Performance Testing

下的apple docs中找到更多信息

答案 2 :(得分:13)

您可以使用此功能来测量异步代码和同步代码:

import Foundation

func measure(_ title: String, block: (@escaping () -> ()) -> ()) {

    let startTime = CFAbsoluteTimeGetCurrent()

    block {
        let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
        print("\(title):: Time: \(timeElapsed)")
    }
}

所以基本上你传递一个接受一个函数作为参数的块,你可以用它来告诉测量什么时候完成。

例如,要测量一些名为“myAsyncCall”的调用所需的时间,您可以这样称呼它:

measure("some title") { finish in
    myAsyncCall {
        finish()
    }
    // ...
}

对于同步代码:

measure("some title") { finish in
     // code to benchmark
     finish()
     // ...
}

这应该类似于XCTest中的measureBlock,虽然我不知道它是如何在那里实现的。

答案 3 :(得分:4)

基准测试功能-Swift 4.2

这是一种用途广泛的基准测试功能,可以对测试进行标记,执行许多测试并平均其执行时间,在测试之间调用设置块(即,在测量排序算法之间对数组进行改组),清除打印基准测试结果,它还会将平均执行时间返回为Double

尝试以下操作:

@_transparent @discardableResult public func measure(label: String? = nil, tests: Int = 1, printResults output: Bool = true, setup: @escaping () -> Void = { return }, _ block: @escaping () -> Void) -> Double {

    guard tests > 0 else { fatalError("Number of tests must be greater than 0") }

    var avgExecutionTime : CFAbsoluteTime = 0
    for _ in 1...tests {
        setup()
        let start = CFAbsoluteTimeGetCurrent()
        block()
        let end = CFAbsoluteTimeGetCurrent()
        avgExecutionTime += end - start
    }

    avgExecutionTime /= CFAbsoluteTime(tests)

    if output {
        let avgTimeStr = "\(avgExecutionTime)".replacingOccurrences(of: "e|E", with: " × 10^", options: .regularExpression, range: nil)

        if let label = label {
            print(label, "▿")
            print("\tExecution time: \(avgTimeStr)s")
            print("\tNumber of tests: \(tests)\n")
        } else {
            print("Execution time: \(avgTimeStr)s")
            print("Number of tests: \(tests)\n")
        }
    }

    return avgExecutionTime
}

用法

var arr = Array(1...1000).shuffled()

measure(label: "Map to String") {
    let _ = arr.map { String($0) }
}

measure(label: "Apple Shuffle", tests: 1000, setup: { arr.shuffle() }) {
    arr.sort()
}

measure {
    let _ = Int.random(in: 1...10000)
}

let mathExecutionTime = measure(printResults: false) {
    let _ = 219 * 354
}

print("Math Execution Time: \(mathExecutionTime * 1000)ms")


// Prints:
//
// Map to String ▿
//     Execution time: 0.021643996238708496s
//     Number of tests: 1
//
// Apple's Sorting Method ▿
//     Execution time: 0.0010601345300674438s
//     Number of tests: 1000
//
// Execution time: 6.198883056640625 × 10^-05s
// Number of tests: 1
//
// Math Execution Time: 0.016927719116210938ms
//

注意measure还返回执行时间。 labeltestssetup参数是可选的。 printResults参数默认设置为true

答案 4 :(得分:3)

我喜欢布拉德·拉尔森(Brad Larson)给出的简单测试答案,您甚至可以在Playground中运行它。为了我自己的需要,我对其进行了一些调整:

  1. 我将对要测试的函数的调用包装在一个测试函数中,这为我提供了必要的空间,可以根据需要使用不同的参数。
  2. 测试功能返回其名称,因此我不必在 public productFamilies: string[] = ['Collector Method', 'Manual Upload','New Assessment']; 基准测试功能中包括“ title”参数。
  3. 通过基准测试功能,您可以选择指定要执行的重复次数(默认为10)。然后报告总时间和平均时间。
  4. 基准测试功能将打印到控制台并返回averageTimeTo()(您可能会期望从名为“ averageTimeTo”的功能获得此功能),因此不需要两个功能几乎相同的独立功能。

例如:

averageTime

答案 5 :(得分:0)

您有几种可能性

  • DispatchTime.now().uptimeNanoseconds(以及div在1_000_000上)

    基于马赫绝对时间单位

壁钟(由于leap秒或其他微校正,不推荐使用)

  • Date().timeIntervalSince1970
  • CFAbsoluteTimeGetCurrent

测试

  • XCTest
let metrics: [XCTMetric] = [XCTMemoryMetric(), XCTStorageMetric(), XCTClockMetric()]

let measureOptions = XCTMeasureOptions.default
measureOptions.iterationCount = 3

measure(metrics: metrics, options: measureOptions) {
    //block of code
}

*我不建议您使用measure块创建通用函数,因为Xcode有时无法正确处理它。这就是为什么对每个性能测试都使用measure

答案 6 :(得分:0)

class TimeMeasurement {
static var shared: TimeMeasurement = TimeMeasurement()
private var start = CFAbsoluteTimeGetCurrent()
func begin() {
    start = CFAbsoluteTimeGetCurrent()
}
@discardableResult func end() -> Double {
    let diff = CFAbsoluteTimeGetCurrent() - start
    print("Took \(diff) seconds")
    return diff
}

}