|  |  |  | // | 
					
						
							|  |  |  | //  Copyright (c) 2019 Open Whisper Systems. All rights reserved. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import Foundation | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// 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) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |