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

tjjdgumg  于 2022-10-04  发布在  Swift
关注(0)|答案(9)|浏览(136)

是否有方法/软件提供执行用SWIFT编写的代码块所需的准确时间?

let date_start = NSDate()

// Code to be executed 

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

u59ebvdq1#

如果您只想为代码块提供独立的计时函数,我可以使用以下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)
}

前者将注销给定代码段所需的时间,后者以浮点数的形式返回该时间。作为第一个变体的示例:

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

将注销类似以下内容:

MAP()所用时间:0.0617449879646301秒

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

r6l8ljro

r6l8ljro2#

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

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

func testExample() {

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

您可以在正在使用Xcode进行测试的Apple文档中找到更多信息->性能测试

qoefvg9y

qoefvg9y3#

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

import Foundation

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

    let startTime = CFAbsoluteTimeGetCurrent()

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

因此,基本上是传递一个接受函数作为参数的块,用它来告诉MEASURE何时结束。

例如,要测量某个名为“myAsyncCall”的调用需要多长时间,您可以这样称呼它:

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

对于同步代码:

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

这应该类似于XCTest中的measure Block,尽管我不知道它是如何在那里实现的。

wbgh16ku

wbgh16ku4#

标杆功能--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(B1a1a1b) }
}

measure(label: "Apple's Sorting Method", 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

vatpfxk5

vatpfxk55#

我喜欢布拉德·拉森对一项简单测试的回答,你甚至可以在Playground上运行。为了我自己的需要,我对它做了一些调整:

1.我已经将对我想要测试的函数的调用 Package 在一个测试函数中,这为我提供了空间,如果我愿意的话,可以使用不同的参数。
1.测试函数返回其名称,因此我不必在averageTimeTo()基准函数中包含‘title’参数。
1.基准功能允许您选择指定要执行的重复次数(默认为10次)。然后,它同时报告总时间和平均时间。
1.基准测试函数打印到控制台并返回averageTime(您可能会从名为‘verageTimeTo’的函数中预料到这一点),因此不需要两个功能几乎相同的独立函数。

例如:

func myFunction(args: Int...) {
    // Do something
}

func testMyFunction() -> String {
    // Wrap the call to myFunction here, and optionally test with different arguments
    myFunction(args: 1, 2, 3)
    return #function
}

// Measure average time to complete test
func averageTimeTo(_ testFunction: () -> String, repeated reps: UInt = 10) -> Double {
    let functionName = testFunction()
    var totalTime = 0.0
    for _ in 0..<reps {
        let startTime = CFAbsoluteTimeGetCurrent()
        testFunction()
        let elapsedTime = CFAbsoluteTimeGetCurrent() - startTime
        totalTime += elapsedTime
    }
    let averageTime = totalTime / Double(reps)
    print("Total time to (functionName) (reps) times: (totalTime) seconds")
    print("Average time to (functionName): (averageTime) secondsn")
    return averageTime
}

averageTimeTo(testMyFunction)

// Total time to testMyFunction() 10 times: 0.000253915786743164 seconds
// Average time to testMyFunction(): 2.53915786743164e-05 seconds

averageTimeTo(testMyFunction, repeated: 1000)

// Total time to testMyFunction() 1000 times: 0.027538537979126 seconds
// Average time to testMyFunction(): 2.7538537979126e-05 seconds
kg7wmglp

kg7wmglp6#

对@Brad Larsons的答案(接受的答案)进行了小小的改进,允许函数在需要时返回操作结果

@discardableResult func printTimeElapsedWhenRunningCode<T>(title: String, operation: () -> T) -> T {
    let startTime = CFAbsoluteTimeGetCurrent()
    let result = operation()
    let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
    print("Time elapsed for (title): (timeElapsed) s.")

    return result
}
bsxbgnwa

bsxbgnwa7#

SWIFT代码执行基准

请在Production版本而不是Debug上进行基准测试,因为它包含更多优化

您有几种可能性

  • DispatchTime.now().uptimeNanoseconds(和1_000_000上的div)

基于马赫绝对时间单位

挂钟(不推荐,因为有闰秒或其他微校准)

  • 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数据块
rsaldnfx

rsaldnfx8#

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
}

}

watbbzwu

watbbzwu9#

A制作了一个很小的SPM包来测量代码执行时间:

https://github.com/mezhevikin/Measure.git

使用示例

Measure.start("create-user")
let user = User()
Measure.finish("create-user")

控制台输出:

⏲ Measure [create-user]: 0.00521 sec.

测量异步请求

Measure.start("request")
let url = URL(string: "https://httpbin.org/get")!
URLSession.shared.dataTask(with: url) { _, _, _ in
    let time = Measure.finish("request")
    Analytics.send(event: "Request", ["time" => time])
}.resume()

包的完整代码:

// Mezhevikin Alexey: https://github.com/mezhevikin/Measure
import Foundation

public class Measure {
    static private var starts = [String: Double]()

    static public func start(_ key: String) {
        starts[key] = CFAbsoluteTimeGetCurrent()
    }

    @discardableResult
    static public func finish(_ key: String) -> Double {
        guard let start = starts[key] else {
            print("🛑 Key [(key)] not found")
            return 0
        }
        let time = CFAbsoluteTimeGetCurrent() - start
        print(String(format: "⏲ Measure [(key)]: %.5f sec.", time))
        starts.removeValue(forKey: key)
        return time
    }
}

相关问题