Чи існує спосіб / програмне забезпечення, щоб дати точний час, необхідний для виконання блоку коду, написаного Swift, крім наступного?
let date_start = NSDate()
// Code to be executed
println("\(-date_start.timeIntervalSinceNow)")
Чи існує спосіб / програмне забезпечення, щоб дати точний час, необхідний для виконання блоку коду, написаного Swift, крім наступного?
let date_start = NSDate()
// Code to be executed
println("\(-date_start.timeIntervalSinceNow)")
Відповіді:
Якщо вам просто потрібна автономна функція синхронізації для блоку коду, я використовую такі допоміжні функції 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($0)), 10.0) }
}
вийде щось на зразок:
Час, що минув для карти (): 0,0617449879646301 с
Майте на увазі, що тести Swift сильно відрізнятимуться залежно від вибраного вами рівня оптимізації , тому це може бути корисним лише для відносного порівняння часу виконання Swift. Навіть це може змінитися залежно від бета-версії.
Якщо ви хочете отримати уявлення про продуктивність певного блоку коду та переконатися, що продуктивність не заважає вам, коли ви редагуєте, найкраще буде використовувати функції вимірювання продуктивності XCTest , наприклад measure(_ block: () -> Void)
.
Напишіть модульний тест, який виконує метод, який ви хочете порівняти, і цей модульний тест буде запускати його кілька разів, даючи вам необхідний час та відхилення результатів
func testExample() {
self.measure {
//do something you want to measure
}
}
Ви можете знайти більше інформації в документації на Apple у розділі Тестування за допомогою Xcode -> Тестування продуктивності
measureBlock
, що виконуватиме блоковий код кілька разів, щоб отримати кращу статистику.
measureBlock
? звідки він береться (що слід імпортувати)? у Swift 4
Ви можете використовувати цю функцію для вимірювання асинхронного, а також синхронного коду:
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()
// ...
}
Це має бути подібним до вимірювання blockBlock від XCTest, хоча я не знаю, як саме це там реалізовано.
Це неймовірно універсальна функція бенчмаркінгу, яка дозволяє маркувати тести, виконувати багато тестів і усереднювати час їх виконання, блок виклику, що викликається між тестами (тобто перемішування масиву між вимірюванням алгоритму сортування на ньому), чіткий друк бенчмаркінгу результати, а також повертає середній час виконання як 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
також повертає час виконання. В label
, tests
і setup
аргументи НЕ є обов'язковими. printResults
Аргумент встановлений true
за замовчуванням.
Мені подобається відповідь Бреда Ларсона на простий тест, який ви навіть можете запустити на дитячому майданчику. Для власних потреб я трохи його підправив:
averageTimeTo()
порівняльного тестування.averageTime
(що можна очікувати від функції, яка називається 'averageTimeTo'), тому немає необхідності в двох окремих функціях, які мають майже однакову функціональність.Наприклад:
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\n")
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
@discardableResult
може бути корисним, оскільки ви вже друкуєтеaverageTime
у вас є кілька можливостей
DispatchTime.now().uptimeNanoseconds
(і div на 1_000_000)
Виходячи з абсолютної одиниці часу Маха
Настінний годинник ( не рекомендується через стрибкову секунду або іншу мікрокорекцію)
Date().timeIntervalSince1970
CFAbsoluteTimeGetCurrent
Тест
let metrics: [XCTMetric] = [XCTMemoryMetric(), XCTStorageMetric(), XCTClockMetric()]
let measureOptions = XCTMeasureOptions.default
measureOptions.iterationCount = 3
measure(metrics: metrics, options: measureOptions) {
//block of code
}
* Я б не пропонував вам створити загальну функцію за допомогою measure
блоком, оскільки Xcode іноді не справляється з цим належним чином. Ось чому використовуйте measure
блок для кожного тесту продуктивності