-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Refactor timers (#11807) 2nd try #11850
Conversation
abea607
to
159564b
Compare
I've somewhere a branch where I changed VSyncThread::fromTimerToNextSyncMicros to an absolute time reference. This absolute time reference was needed in the sounddevice implementation anyway, because Abeton-Link needs a jitter-free absolute clock reference. And the filtering to get this jitter-free clock requires an absolute monotonic clock. |
great. could you try to dig that up? Then we can merge it independently as a per-requisite. |
I found it: https://github.com/JoergAtGithub/mixxx/tree/visualPlaypositionAbsoluteTime |
Mhmm okay, so what's your preferred plan of action? Just implement a more basic |
The Ableton Link code uses the following platform specific monotonic clock implementations: |
right, so lets just trust the stdlib implementors and use |
I am confused. From https://doc.qt.io/qt-6/qelapsedtimer.html#nsecsElapsed It looks like that QElapsedTimer gives us a nanoseconds resolution if possible. Before this commit, Qt uses the desired low level API as before: Does this involve a regression? |
I think Yes :-( it uses the "std::chrono::steady_clock" which is know to may have a low resolution: https://stackoverflow.com/questions/29551126/does-steady-clock-have-only-10ms-resolution-on-windows-cygwin Such high level APIs break at any time just like the QElapsedTimer in QT 6.6 because we rely on undefined behavior. What was the issue with our original Implementation? Can we just revert to the original low level API call with guaranteed behaviors and fix the issues there? Alternative we may copy the QT < 6.6 implementation to our source tree and extend it by a "nsecsTo" or "durationTo()" |
Yes, but it only gives us the elapsed time at the time of calling. In other words, it only gives us the duration between when the timer was started and at the time of calling
Right now, I'm currently opting for 2. which entails a |
Nothing much, I just wanted to clean it up because nobody has looked at it since the last 10 years (and probably won't for another 10).
thats unfortunate, but in proper implementation this should be checkable statically:
Can you elaborate on the undefined behavior you are eluding to? |
The standard doe not define a resolution nor an overflow behavior. Yes, it provides a API to peek it like you proposed, but that need to be done at runtime, because it depends on the OS and std implementation. Given that we have a reliable low level API code, it does not make much sense to give away control to glue layers and than take the efforts to investigate if the implementation suits our needs. This is too much work IMHO but can of cause be done. So I would either tidy our own established PerformanceTimer or copy the QElapsedTimer before QT6.6 instead and add the missing function. |
Why can't we do this at compile time? We know the target OS and the target std lib at compile time. After all, thats how the platform specific APIs are switched out at compile time too (using |
not really. could even be kept just as concise with a small helper function.
Right, but |
I still don't like While reworking vsyncthread and in turn visualplayposition, we're passing a PerformanceTimer object through multiple layers (possibly threads even (unguarded!!!)) just in order to eventually call there is no conceptual reason for |
Because we use the same binary in different OS versions.
What is the resolution on Linux. What was the resolution of our low level implementation. I don't think it is reasonable to pick a timer with a lower resolution and a glue layer that shifts the overflow artificialy. We should aim for the fastest solution with maximum precision. I still don't like "difference()' Qt 6.6 has durationTo() which fulfils the same use case. Maybe we can introduce that. PerformanceTimer is nothing else than a timestamp. It can be passed around as you like. durationTo() is just a renamed operator-() implementation. |
Okay, so lets come to a compromise. We keep the previous low-level implementation but implement the high-level interface using
Under the hood its a timestamp yes, but it does not have the value semantics you would expect from a timestamp. As such, we should avoid passing it around as such!
Let me investigate, I doubt there is any difference with libstdc++'s timer implementation. |
This is my crude test, suggesting that the difference between |
You test measures the duration of the loop. I have modified it to measure the duration of two consecutive timer calls compared to the low level API: https://compiler-explorer.com/z/1PnaTqWGE I think we see that we have ns resolution in both cases.
Thank you, that works for me.
The trick here is, that we compare the highest resolution value that will naturally overflow. If you store the value scaled as a chrono ns type you loose it. That's why |
Right... so, using chrono clocks instead of the lowlevel ones is an option after all?
Right, my point is that we should not compare and pass around Timers but instead |
fyi, digging deeper in the low-level APIs, there is no guarantee over the precision of timers on macOS either as far as I can tell... I still think we just should use mixxx/src/util/performancetimer.cpp Lines 272 to 292 in dfde28d
|
What are your reasons to use it?
Since our PerformanceTimer is basically a time stamp I do not see an abuse here. But of cause we can warp the timestamp part of PerformanceTime into a PerformanceTimeStamp class or such and pass that around instead.
I don't get that point. You need always build the difference to get a human readable value. That is exactly the same with We have good experience with the low level timers and no reported issues with it. So we need some good reasons to replace that with a additional abstraction layer that takes more cpu time, developer and review resources and introduces additional dependencies we can't control. |
Less code, less maintenance. Makes it easier to benefit from the modern abstractions.
Yes, I'd be more okay with a method that extracts a |
Don't touching the code without an issue is the minimum of maintenance. We don't make use of the modern abstraction or have any benefit or demands. I think creating If you like to use the modern abstraction in places where precision and performance is not a curlpit, you can already use plain QElapsedTimer or the Chrono functions. There is no reason to wrap them. I consider using a Mixxx function more dificult, than a QT or Std API function Did you consider to spit out PerformanceTimeStamp form PerformanceTimer? For my understanding that targets your concern the best. I fully agree that passing something around that is called "Timer" looks odd. Something like this:
|
159564b
to
d7efeb8
Compare
What cost?
Why not use |
I agree. That test will get brittle really fast. I've already observed in the timers-benchmark branch that the median duration of subsequent
|
0f6a214
to
6abbb28
Compare
6abbb28
to
90825e3
Compare
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.
Looks good now.
Before merge you nee to remove the Draft state.
90825e3
to
70107e2
Compare
70107e2
to
5523d9b
Compare
Thank you. Just tidied up the test a little and amended. |
5523d9b
to
865bae8
Compare
ofcourse, ancient AppleClang has no C++20 ranges support... |
all green, no draft. lets go |
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.
Thank you. LGTM.
Thank you. And lets please reduce the arguing over unnecessary concerns in the future. |
I will also take the liberty to remove the steady_clock test if it turns out to be brittle. |
So, this is #11807 reopened after I reset it out of the branch. Unfortunately this PR does suffer a significant issue: Due to Qt API limitations,
PerformanceTimer::difference
can not provide the same resolution as previously.Fortunately, the only real consumer of that function is
VSyncThread::fromTimerToNextSyncMicros
. Unfortunately, the resolution provided by Qt (Milliseconds) does not suffice in the context of frame timings:mixxx/src/waveform/vsyncthread.cpp
Lines 120 to 124 in dfde28d
I'd prefer to just get rid of
PerformanceTimer::difference
since it doesn't really have much to do with Timers anyways and just usestd::chrono::time_point
in theVSyncThread
with the appropriatestd::chrono
clock. The alternative would be to drop the QElapsedTimer commit and just use thestd::chrono
-based implementation ofPerformanceTimer
. wdyt? @daschuerMarking PR as draft until we discussed a solution and the PR is ready to be merged.