How to benchmark Swift code execution?
是否有一种方法/软件提供执行用swift编写的代码块所需的精确时间,但以下情况除外?
1 2 3 4 5 | let date_start = NSDate() // Code to be executed println("\(-date_start.timeIntervalSinceNow)") |
如果您只需要一个代码块的独立计时功能,我使用以下快捷助手功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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) } |
前者将记录给定代码段所需的时间,后者将其作为浮点返回。作为第一个变体的例子:
1 2 3 | printTimeElapsedWhenRunningCode(title:"map()") { let resultArray1 = randoms.map { pow(sin(CGFloat($0)), 10.0) } } |
将注销如下内容:
Time elapsed for map(): 0.0617449879646301 s
请注意,根据您选择的优化级别,Swift基准会有很大的不同,因此这可能只对Swift执行时间的相对比较有用。即使是在每个测试版的基础上,这也可能发生变化。
如果您想深入了解某个代码块的性能,并确保在进行编辑时性能不会受到影响,最好是使用XCEST的度量性能函数,如
编写一个单元测试来执行您想要基准测试的方法,该单元测试将多次运行它,给您所需的时间和结果的偏差。
1 2 3 4 5 6 | func testExample() { self.measure { //do something you want to measure } } |
您可以在Apple文档中找到更多信息,这些文档正在使用xcode->性能测试进行测试。
您可以使用此函数测量异步和同步代码:
1 2 3 4 5 6 7 8 9 10 11 | import Foundation func measure(_ title: String, block: (@escaping () -> ()) -> ()) { let startTime = CFAbsoluteTimeGetCurrent() block { let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime print("\(title):: Time: \(timeElapsed)") } } |
所以基本上你传递一个接受函数作为参数的块,它用来告诉度量什么时候结束。
例如,要测量某个名为"myAsyncCall"的调用需要多长时间,您可以这样调用它:
1 2 3 4 5 6 | measure("some title") { finish in myAsyncCall { finish() } // ... } |
对于同步代码:
1 2 3 4 5 | measure("some title") { finish in // code to benchmark finish() // ... } |
这应该类似于XCEST中的MeasureBlock,尽管我不知道它在那里的实现方式。
基准功能-Swift 4.2
这是一个非常通用的基准测试功能,允许对测试进行标记、执行许多测试并平均执行时间、在测试之间调用设置块(即在测试上测量排序算法之间切换数组)、清晰打印基准测试结果,还返回平均执行时间。我是一个江户。
请尝试以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | @_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) ") } else { print("Execution time: \(avgTimeStr)s") print("Number of tests: \(tests) ") } } return avgExecutionTime } |
用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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 // |
注:
我喜欢布拉德·拉森对一个简单测试的回答,你甚至可以在操场上跑步。为了我自己的需要,我做了一些调整:
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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) seconds ") 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 |