Initial implementation of Stopwatch - #161817
ChrisDenton wants to merge 2 commits into
Conversation
f1788d9 to
8c3406c
Compare
|
Huh, I'm not actually sure about apple. I thought |
7983c80 to
2c64ef2
Compare
|
@bors try jobs=aarch64-apple-* |
This comment has been minimized.
This comment has been minimized.
Initial implementation of `Stopwatch` try-job: aarch64-apple-*
This comment has been minimized.
This comment has been minimized.
It is monotonic, but it's measured by subtracting the system boot time from |
| /// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] | | ||
| /// | UNIX | [clock_gettime] with `CLOCK_MONOTONIC` | | ||
| /// | WASI | [clock_gettime] with `CLOCK_MONOTONIC` | | ||
| /// | Darwin | [clock_gettime] with `CLOCK_MONOTONIC` | |
There was a problem hiding this comment.
This will need updating...
| /// The following system calls are [currently] being used by `now()` to find out | ||
| /// the current time: | ||
| /// | ||
| /// | Platform | System call | |
There was a problem hiding this comment.
I wonder whether it'd be easier to only mention the platforms where Stopwatch deviates from Instant.
There was a problem hiding this comment.
I think if/when this is stable it'd be good to consulate some of these docs at the module level. But in the meantime I guess it makes sense to keep duplication to a minimum.
There was a problem hiding this comment.
Oh yeah, module-level sounds great! I've just noticed a tendency for these tables to fall out of sync with the actual implementation, so keeping duplication to a minimum hopefully helps prevent that.
There was a problem hiding this comment.
I got a bit side tracked by trying to optimise the windows performance counter. I realised if you give the compiler enough information it will optimise down to a single imul instruction on x86/x64. It's less dramatic on arm64 but still an improvement. See godbolt. The values of 10_000_000 and 24_000_000 for frequency apply to x86/x64 and aarch64 respectively. This will cover most real-world cases.
Using niche types might be a bit OTT but having the information be carried by the types does seem more natural to me than assert_unchecked.
| let mut qpc_value: i64 = 0; | ||
| // `QueryPerformanceCounter` will never fail (since XP). | ||
| unsafe { c::QueryPerformanceCounter(&mut qpc_value) }; | ||
| // SAFETY: QueryPerformanceCounter returns a positive integer. |
There was a problem hiding this comment.
Could you add a reference for that? E.g. https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps saying that "QueryPerformanceCounter reads the performance counter and returns the total number of ticks that have occurred since the Windows operating system was started".
| pub fn checked_sub(self, other: Self) -> Option<Self> { | ||
| self.as_u64() | ||
| .checked_sub(other.as_u64()) | ||
| // SAFETY: u64::checked_sub ensures the result will be >=0 and <=i64::MAX. |
There was a problem hiding this comment.
It's not u64::checked_sub that ensures <= i64::MAX, but the fact that both self and other are positive, so I found this a bit misleading.
There was a problem hiding this comment.
Sure. I was thinking of it in terms of the as cast's affect on converting an u64 to a i64 (so checked_sub prevents the negative case) but I can see that's confusing.
| } else if freq == 24_000_000 && cfg!(target_arch = "aarch64") { | ||
| mul_div_u64(counter, magnitude, freq) |
There was a problem hiding this comment.
Ok, so this is funky on aarch64. I need the freq == 10_100_000 branch (even if it's never taken) otherwise the codegen actually gets worse: https://rust.godbolt.org/z/cd4eE1edT
| } | ||
|
|
||
| pub fn checked_duration_since(self, other: Stopwatch) -> Option<Duration> { | ||
| let diff = self.ticks.checked_sub(other.ticks)?; |
There was a problem hiding this comment.
This might make this function return None in the cross-thread case, whereas Instant would return Duration::ZERO.
|
|
||
| pub fn checked_duration_since(self, other: Stopwatch) -> Option<Duration> { | ||
| let diff = self.ticks.checked_sub(other.ticks)?; | ||
| if diff.as_u64() <= 1 { |
There was a problem hiding this comment.
I think it's better to return the proper difference in this case, there is little harm in returning it here. The case where other is self + 1 is the one where I'd return Duration::ZERO. But anyhow, I'm not sure if the note in the documentation still applies, I can well imagine it to be outdated at this point – especially given that none of the other operating systems seem to have problems with cross-thread clock synchronisation.
There was a problem hiding this comment.
Hm, I'll want to investigate some more but I am minded to just remove the +/- 1 case.
6876bad to
c0ea3fd
Compare
View all comments
For most platforms this currently delegates to
Instantbut they could drift apart later. For Linux I consideredCLOCK_MONOTONIC_RAWbut I wasn't fully convinced it was the right thing to do since adjustments do makeCLOCK_MONOTONICmore accurate (and apparently there's a bug in older kernels that make calls toCLOCK_MONOTONIC_RAWslower). Windows currently usesQueryPerformanceCounterfor bothInstantandStopwatchbut without the need to convert to a duration as soon as getting the counter. It is expected forInstantto change in the future to use the same clock as timeouts, etc.Tracking issue: #161809
r? joboet