Skip to content

Instantly share code, notes, and snippets.

@MaximKotliar
Last active March 17, 2021 12:32
Show Gist options
  • Save MaximKotliar/d38fd4e79dc58c425d7e47fea460b089 to your computer and use it in GitHub Desktop.
Save MaximKotliar/d38fd4e79dc58c425d7e47fea460b089 to your computer and use it in GitHub Desktop.
public final class Stopwatch {
var start: DispatchTime = .now()
let maxTime: TimeInterval
let measurementTitle: String
private var isReportedAtLeastOnce = false
@discardableResult
static func create(title: String = #function, maxTime: TimeInterval = 0) -> Stopwatch {
Self.init(title: title, maxTime: maxTime)
}
private init(title: String = #function, annotation: String? = nil, maxTime: TimeInterval = 0) {
self.measurementTitle = title
self.maxTime = maxTime
}
var time: TimeInterval {
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
return Double(nanoTime) / Double(NSEC_PER_SEC)
}
private func reportTime(time: TimeInterval, message: String? = nil) {
debugPrint("⏱ [\([measurementTitle, message].compactMap { $0 }.joined(separator: ", "))]: \(NSNumber(value: time).decimalValue)s")
}
func report(message: String? = nil) {
isReportedAtLeastOnce = true
reportTime(time: self.time, message: message)
}
func reportAndReset(message: String? = nil) {
report(message: message)
start = .now()
}
func reportIfNeeded(message: String? = nil) {
let time = self.time
guard time > maxTime else { return }
reportTime(time: time, message: message)
}
func callAsFunction() {
report()
}
deinit {
guard !isReportedAtLeastOnce else { return }
reportIfNeeded()
}
}
import UIKit
final class FrameDropMonitor {
private static var current: FrameDropMonitor?
private var displayLink: CADisplayLink?
private var lastRenderTime: DispatchTime = .now()
init() {
let maxTime = TimeInterval(1) / TimeInterval(UIScreen.main.maximumFramesPerSecond)
displayLink = CADisplayLink { [unowned self] in
let now = DispatchTime.now()
let timeSinceLastRender = TimeInterval(now.uptimeNanoseconds - self.lastRenderTime.uptimeNanoseconds) / TimeInterval(NSEC_PER_SEC)
if timeSinceLastRender.milliseconds > maxTime.milliseconds + 1 {
debugPrint("Frame drop, render time is: \(timeSinceLastRender.milliseconds)ms")
}
self.lastRenderTime = now
}
displayLink?.add(to: .current, forMode: .common)
}
deinit {
displayLink?.invalidate()
}
static func start() {
current = FrameDropMonitor()
}
static func stop() {
current = nil
}
}
@MaximKotliar
Copy link
Author

MaximKotliar commented Jan 22, 2021

Usage:

let sw = Stopwatch.create()
// Task A to measure
sw.reportAndReset("Task A")
// Task B to measure
sw.reportAndReset("Task B")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment