-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introducing Timer.record(_ duration:), closes #114 #133
Changes from 6 commits
37ae7aa
e0ce4e8
d0bd9aa
9cccde4
b019460
02fc732
f8316f2
9b835a7
fd5b5d2
84db69d
99ec049
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -74,3 +74,39 @@ extension Timer { | |
} | ||
} | ||
} | ||
|
||
#if swift(>=5.7) | ||
|
||
public enum TimerError: Error { | ||
case durationToIntOverflow | ||
} | ||
|
||
extension Timer { | ||
/// Convenience for recording a duration based on ``Duration``. | ||
/// | ||
natikgadzhi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// `Duration` will be converted to an `Int64` number of nanoseconds, and then recorded with nanosecond precision. | ||
/// | ||
/// - Parameters: | ||
/// - duration: The `Duration` to record. | ||
/// | ||
/// - Throws: `TimerError.durationToIntOverflow` if conversion from `Duration` to `Int64` of Nanoseconds overflowed. | ||
@available(macOS 13, iOS 16, tvOS 15, watchOS 8, *) | ||
@inlinable | ||
public func record(_ duration: Duration) throws { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not convinced we want these to throw as it will make the call sites awkward to use. I think we should record There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can do that! The way I understand the trade-off:
Do I understand the problem correctly? I'll clean up a code in a bit when I have a minute. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. exactly. we already take that approach in
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tomerd, pushed up a change. Performing overflow checks one at a time to not perform the second operation if the first already overflowed. I don't yet know the separation into Is it important where that Timer extension lives? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aside: I know an engineer at Apple could put that PR and merge it in under an hour, and I appreciate your patience and guidance, @tomerd. |
||
// `Duration` doesn't have a nice way to convert it nanoseconds or seconds, | ||
// so we'll do the multiplication manually. | ||
// Nanoseconds are the smallest unit Timer can track, so we'll record in that. | ||
let (seconds, overflow) = duration.components.seconds.multipliedReportingOverflow(by: 1_000_000_000) | ||
guard !overflow else { | ||
throw TimerError.durationToIntOverflow | ||
} | ||
|
||
let (nanoseconds, attosecondsOverflow) = seconds.addingReportingOverflow(duration.components.attoseconds / 1_000_000_000) | ||
guard !attosecondsOverflow else { | ||
throw TimerError.durationToIntOverflow | ||
} | ||
|
||
self.recordNanoseconds(nanoseconds) | ||
} | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,6 +96,35 @@ class MetricsExtensionsTests: XCTestCase { | |
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored") | ||
} | ||
|
||
func testTimerDuration() throws { | ||
// Wrapping only the insides of the test case so that the generated | ||
// tests on Linux in MetricsTests+XCTest don't complain that the func does not exist. | ||
#if swift(>=5.7) | ||
guard #available(iOS 16, macOS 13, tvOS 15, watchOS 8, *) else { | ||
return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use XCTSkip or XCTSkipIf where we are skipping the test? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL. |
||
} | ||
|
||
let metrics = TestMetrics() | ||
MetricsSystem.bootstrapInternal(metrics) | ||
|
||
let name = "timer-\(UUID().uuidString)" | ||
let timer = Timer(label: name) | ||
|
||
let duration = Duration(secondsComponent: 3, attosecondsComponent: 123_000_000_000_000_000) | ||
let durationInNanoseconds = duration.components.seconds * 1_000_000_000 + duration.components.attoseconds / 1_000_000_000 | ||
|
||
XCTAssertNoThrow(try timer.record(duration)) | ||
|
||
let testTimer = try metrics.expectTimer(timer) | ||
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match") | ||
XCTAssertEqual(testTimer.values.first, durationInNanoseconds, "expected value to match") | ||
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored") | ||
|
||
let overflowDuration = Duration(secondsComponent: 10_000_000_000, attosecondsComponent: 123) | ||
XCTAssertThrowsError(try timer.record(overflowDuration)) | ||
#endif | ||
} | ||
|
||
func testTimerUnits() throws { | ||
let metrics = TestMetrics() | ||
MetricsSystem.bootstrapInternal(metrics) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can be removed now?