Merge c320ae32c1 into 971ba26378
This commit is contained in:
commit
98f810cae2
|
|
@ -24,6 +24,7 @@ extension Timer {
|
|||
/// - dimensions: The dimensions for the Timer.
|
||||
/// - body: Closure to run & record.
|
||||
@inlinable
|
||||
@available(*, deprecated, message: "Please use non-static version on an already created Timer")
|
||||
public static func measure<T>(label: String, dimensions: [(String, String)] = [], body: @escaping () throws -> T) rethrows -> T {
|
||||
let timer = Timer(label: label, dimensions: dimensions)
|
||||
let start = DispatchTime.now().uptimeNanoseconds
|
||||
|
|
@ -74,3 +75,88 @@ extension Timer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if (os(macOS) && swift(>=5.7.1)) || (!os(macOS) && swift(>=5.7))
|
||||
extension Timer {
|
||||
/// Convenience for recording a duration based on Duration.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - duration: The duration to record.
|
||||
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||
public func record(duration: Duration) {
|
||||
self.recordNanoseconds(duration.nanosecondsClamped)
|
||||
}
|
||||
|
||||
/// Convenience for recording a duration since Instant using provided Clock
|
||||
///
|
||||
/// - parameters:
|
||||
/// - instant: The instant to measure duration since
|
||||
/// - clock: The clock to measure duration with
|
||||
@inlinable
|
||||
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||
public func recordDurationSince<C: Clock>(
|
||||
instant: C.Instant,
|
||||
clock: C = ContinuousClock.continuous
|
||||
) where C.Duration == Duration {
|
||||
self.record(duration: instant.duration(to: clock.now))
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||
internal extension Swift.Duration {
|
||||
/// The duration represented as nanoseconds, clamped to maximum expressible value.
|
||||
var nanosecondsClamped: Int64 {
|
||||
let components = self.components
|
||||
|
||||
let secondsComponentNanos = components.seconds.multipliedReportingOverflow(by: 1_000_000_000)
|
||||
let attosCompononentNanos = components.attoseconds / 1_000_000_000
|
||||
let combinedNanos = secondsComponentNanos.partialValue.addingReportingOverflow(attosCompononentNanos)
|
||||
|
||||
guard
|
||||
!secondsComponentNanos.overflow,
|
||||
!combinedNanos.overflow
|
||||
else {
|
||||
return .max
|
||||
}
|
||||
|
||||
return combinedNanos.partialValue
|
||||
}
|
||||
}
|
||||
|
||||
extension Timer {
|
||||
/// Convenience for measuring duration of a closure
|
||||
///
|
||||
/// - parameters:
|
||||
/// - body: Closure to run & record.
|
||||
@inlinable
|
||||
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||
public func measure<T>(
|
||||
body: @escaping () throws -> T
|
||||
) rethrows -> T {
|
||||
let start = ContinuousClock.continuous.now
|
||||
defer {
|
||||
self.recordDurationSince(instant: start, clock: ContinuousClock.continuous)
|
||||
}
|
||||
return try body()
|
||||
}
|
||||
|
||||
/// Convenience for measuring duration of an async closure with a provided clock
|
||||
///
|
||||
/// - parameters:
|
||||
/// - clock: The clock to measure closure duration with
|
||||
/// - body: Closure to run & record.
|
||||
@inlinable
|
||||
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
|
||||
public func measure<T, C: Clock>(
|
||||
clock: C = ContinuousClock.continuous,
|
||||
body: @escaping () async throws -> T
|
||||
) async rethrows -> T where C.Duration == Duration {
|
||||
let start = clock.now
|
||||
defer {
|
||||
self.recordDurationSince(instant: start, clock: clock)
|
||||
}
|
||||
return try await body()
|
||||
}
|
||||
}
|
||||
|
||||
#endif // (os(macOS) && swift(>=5.7.1)) || (!os(macOS) && swift(>=5.7))
|
||||
|
|
|
|||
|
|
@ -152,6 +152,74 @@ class MetricsExtensionsTests: XCTestCase {
|
|||
testTimer.preferDisplayUnit(.days)
|
||||
XCTAssertEqual(testTimer.valueInPreferredUnit(atIndex: 0), value / (60 * 60 * 24), accuracy: 0.000000001, "expected value to match")
|
||||
}
|
||||
|
||||
#if (os(macOS) && swift(>=5.7.1)) || (!os(macOS) && swift(>=5.7))
|
||||
func testTimerBlock() async throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
// run the test
|
||||
let name = "timer-\(UUID().uuidString)"
|
||||
let delay = Duration.milliseconds(5)
|
||||
let timer = Timer(label: name)
|
||||
try await timer.measure {
|
||||
try await Task.sleep(for: delay)
|
||||
}
|
||||
let expectedTimer = try metrics.expectTimer(name)
|
||||
XCTAssertEqual(1, expectedTimer.values.count, "expected number of entries to match")
|
||||
XCTAssertGreaterThan(expectedTimer.values[0], delay.nanosecondsClamped, "expected delay to match")
|
||||
}
|
||||
|
||||
func testTimerWithDuration() throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
// run the test
|
||||
let name = "test-timer"
|
||||
let timer = Timer(label: name)
|
||||
let duration = Duration.milliseconds(5)
|
||||
timer.record(duration: duration)
|
||||
|
||||
let expectedTimer = try metrics.expectTimer(name)
|
||||
XCTAssertEqual(1, expectedTimer.values.count, "expected number of entries to match")
|
||||
XCTAssertEqual(expectedTimer.values[0], duration.nanosecondsClamped, "expected delay to match")
|
||||
}
|
||||
|
||||
func testTimerWithDurationOnContinuousClock() async throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
// run the test
|
||||
let name = "test-timer"
|
||||
let timer = Timer(label: name)
|
||||
let clock = ContinuousClock()
|
||||
let start = clock.now
|
||||
let duration = Duration.milliseconds(5)
|
||||
try await Task.sleep(for: duration)
|
||||
timer.recordDurationSince(instant: start, clock: clock)
|
||||
|
||||
let expectedTimer = try metrics.expectTimer(name)
|
||||
XCTAssertEqual(1, expectedTimer.values.count, "expected number of entries to match")
|
||||
XCTAssertGreaterThan(expectedTimer.values[0], duration.nanosecondsClamped, "expected delay to match")
|
||||
}
|
||||
|
||||
func testTimerWithDurationOnDefaultContinuousClock() async throws {
|
||||
// bootstrap with our test metrics
|
||||
let metrics = TestMetrics()
|
||||
MetricsSystem.bootstrapInternal(metrics)
|
||||
// run the test
|
||||
let name = "test-timer"
|
||||
let timer = Timer(label: name)
|
||||
let start = ContinuousClock.now
|
||||
let duration = Duration.milliseconds(5)
|
||||
try await Task.sleep(for: duration)
|
||||
timer.recordDurationSince(instant: start)
|
||||
|
||||
let expectedTimer = try metrics.expectTimer(name)
|
||||
XCTAssertEqual(1, expectedTimer.values.count, "expected number of entries to match")
|
||||
XCTAssertGreaterThan(expectedTimer.values[0], duration.nanosecondsClamped, "expected delay to match")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// https://bugs.swift.org/browse/SR-6310
|
||||
|
|
|
|||
Loading…
Reference in New Issue