session-ios/SignalUtilitiesKit/Utilities/Bench.swift

116 lines
3.1 KiB
Swift

// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
import Foundation
import SignalCoreKit
/// Benchmark async code by calling the passed in block parameter when the work
/// is done.
///
/// BenchAsync(title: "my benchmark") { completeBenchmark in
/// foo {
/// // consider benchmarking of "foo" complete
/// completeBenchmark()
///
/// // call any completion handler foo might have
/// fooCompletion()
/// }
/// }
public func BenchAsync(title: String, block: (@escaping () -> Void) -> Void) {
let startTime = CACurrentMediaTime()
block {
let timeElapsed = CACurrentMediaTime() - startTime
let formattedTime = String(format: "%0.2fms", timeElapsed * 1000)
Logger.debug("[Bench] title: \(title), duration: \(formattedTime)")
}
}
public func Bench(title: String, block: () -> Void) {
BenchAsync(title: title) { finish in
block()
finish()
}
}
public func Bench(title: String, block: () throws -> Void) throws {
var thrownError: Error?
BenchAsync(title: title) { finish in
do {
try block()
} catch {
thrownError = error
}
finish()
}
if let errorToRethrow = thrownError {
throw errorToRethrow
}
}
/// When it's not convenient to retain the event completion handler, e.g. when the measured event
/// crosses multiple classes, you can use the BenchEvent tools
///
/// // in one class
/// BenchEventStart(title: "message sending", eventId: message.id)
/// beginTheWork()
///
/// ...
///
/// // in another class
/// doTheLastThing()
/// BenchEventComplete(eventId: message.id)
///
/// Or in objc
///
/// [BenchManager startEventWithTitle:"message sending" eventId:message.id]
/// ...
/// [BenchManager completeEventWithEventId:eventId:message.id]
public func BenchEventStart(title: String, eventId: BenchmarkEventId) {
BenchAsync(title: title) { finish in
runningEvents[eventId] = Event(title: title, eventId: eventId, completion: finish)
}
}
public func BenchEventComplete(eventId: BenchmarkEventId) {
guard let event = runningEvents.removeValue(forKey: eventId) else {
Logger.debug("no active event with id: \(eventId)")
return
}
event.completion()
}
public typealias BenchmarkEventId = String
private struct Event {
let title: String
let eventId: BenchmarkEventId
let completion: () -> Void
}
private var runningEvents: [BenchmarkEventId: Event] = [:]
@objc
public class BenchManager: NSObject {
@objc
public class func startEvent(title: String, eventId: BenchmarkEventId) {
BenchEventStart(title: title, eventId: eventId)
}
@objc
public class func completeEvent(eventId: BenchmarkEventId) {
BenchEventComplete(eventId: eventId)
}
@objc
public class func benchAsync(title: String, block: (@escaping () -> Void) -> Void) {
BenchAsync(title: title, block: block)
}
@objc
public class func bench(title: String, block: () -> Void) {
Bench(title: title, block: block)
}
}