-
-
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
Refactor timers #11807
Conversation
// This is a fork of QPerformanceTimer just without the Q prefix | ||
// To fix interface changes issues in different QT versions | ||
// Added restart() function. |
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.
Have you checked if this class is still necessary? Maybe we can use plain QPerformanceTimer
nowadays.
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.
QPerformanceTimer doesn't exist anymore as far as I can tell. Closest thing would be QElapsedTimer. Didn't investigate whether that satisfies the same constraints. I will check.
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.
QPerformanceTimer was forked from QElapsedTimer and later reintegrated:
qt/qt@8896651
I think we can adopt that commit to our code and drop our PerformanceTimer entirely.
I recommend that over the std::chrono::steady_clock solution because it is only weak defined by the C++ standard and may change by any update or different standard library.
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.
Unfortunately the API differs a little bit because qt has no duration type. PerformanceTimer
is also spread over more than 40 classes in our codebase. IMO we should keep PerformanceTimer as a facade over QElapsedTimer that makes use of mixxx::Duration
(which we should also replace with std::chrono::duration` IMO but thats even harder than replacing PerformanceTimer verbatim).
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.
Should we add a task to our Semantic Type Refactoring project https://github.com/mixxxdj/mixxx/projects/5 ?
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.
Maybe. We should at least think about replacing the mixxx::Duration
internals with std::chrono::duration
to allow for gradual transition. But mixxx::Duration
is already a semantic type, so I don't see a pressing need. The only major advantage std::chrono::duration
has is its duration_cast
as far as I can tell.
src/util/timer.h
Outdated
if (m_state) { | ||
m_state->cancelled = true; | ||
} |
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.
The cancelled state can be replaced by m_state == std::nullopt;
if (m_state) { | |
m_state->cancelled = true; | |
} | |
m_state.reset(); |
Because the timers is no longer required and can be destructed.
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.
I'm not super familiar with C++ destructors but I thought this would still happen automatically for RAII types even when using a user-provided constructor. Or is there a way to call a default-generated destructor within the user-provided destructor?
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.
The cancelled state can be replaced by m_state == std::nullopt;
That implies the removal of the cancel
method, but its only used in one location and thats before a return, so basically useless anyway.
src/util/timer.h
Outdated
initialize(QString(key), arg ? QString(arg) : QString(), compute); | ||
} | ||
ScopedTimer(QStringView key, int i, Stat::ComputeFlags compute = kDefaultComputeFlags) | ||
: ScopedTimer(key, QString::number(i), compute) { |
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.
QString::number(i) constructs a QString without --developer.
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.
fixed, now results in a duplicate getDeveloper()
call, but I'm pretty sure that gets optimized away.
// This is a fork of QPerformanceTimer just without the Q prefix | ||
// To fix interface changes issues in different QT versions | ||
// Added restart() function. |
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.
QPerformanceTimer was forked from QElapsedTimer and later reintegrated:
qt/qt@8896651
I think we can adopt that commit to our code and drop our PerformanceTimer entirely.
I recommend that over the std::chrono::steady_clock solution because it is only weak defined by the C++ standard and may change by any update or different standard library.
src/util/timer.h
Outdated
~ScopedTimer() noexcept { | ||
if (m_state) { | ||
if (!m_state->cancelled) { | ||
m_state->timer.elapsed(true); | ||
} | ||
if (m_maybeTimer) { | ||
m_maybeTimer->elapsed(true); | ||
} | ||
m_maybeTimer.reset(); | ||
} |
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.
The reset triggers a -Wmaybe-uninitialized
warning locally for me and I'm not sure why...
In destructor ‘QString::~QString()’,
inlined from ‘Timer::~Timer()’ at /home/swiftb0y/Sourcerepositories/mixxx/src/util/timer.h:20:7,
inlined from ‘constexpr void std::_Optional_payload_base<_Tp>::_M_destroy() [with _Tp = Timer]’ at /usr/include/c++/13/optional:287:35,
inlined from ‘constexpr void std::_Optional_payload_base<_Tp>::_M_reset() [with _Tp = Timer]’ at /usr/include/c++/13/optional:318:14,
inlined from ‘constexpr std::_Optional_payload<_Tp, false, _Copy, _Move>::~_Optional_payload() [with _Tp = Timer; bool _Copy = false; bool _Move = false]’ at /usr/include/c++/13/optional:439:65,
inlined from ‘constexpr std::_Optional_base<Timer, false, false>::~_Optional_base()’ at /usr/include/c++/13/optional:510:12,
inlined from ‘constexpr std::optional<Timer>::~optional()’ at /usr/include/c++/13/optional:705:11,
inlined from ‘ScopedTimer::~ScopedTimer()’ at /home/swiftb0y/Sourcerepositories/mixxx/src/util/timer.h:71:5,
inlined from ‘virtual void VinylControlXwax::analyzeSamples(CSAMPLE*, size_t)’ at /home/swiftb0y/Sourcerepositories/mixxx/src/vinylcontrol/vinylcontrolxwax.cpp:648:1:
/usr/include/qt5/QtCore/qstring.h:1311:35: warning: ‘*(QString*)((char*)&t + offsetof(ScopedTimer, ScopedTimer::m_maybeTimer.std::optional<Timer>::<unnamed>.std::_Optional_base<Timer, false, false>::<unnamed>)).QString::d’ may be used uninitialized [-Wmaybe-uninitialized]
1311 | inline QString::~QString() { if (!d->ref.deref()) Data::deallocate(d); }
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.
The Uninitialized QString seems to be Timer::m_key. The warning happens after the user destructor in the auto generated code.
It is uninitialized if m_maybeTimer is std::nullopt. But in that case the ~Timer() not be run.
93a5644
to
482a425
Compare
The QElapsedTimer::clockType() == PerformanceTimer debug assert fails on some platforms... How do we go about that? relax the restrictions and just insist its monotonic? |
According to the documentation QElapsedTimer::PerformanceCounter is a Windows implementation: |
src/util/timer.h
Outdated
} | ||
m_maybeTimer.reset(); |
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.
This will happen automatically.
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.
Are you sure? Thats what I was asking in this comment: #11807 (comment)
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.
Yes, the back trace with the warning #11807 (comment) proves it.
But there seems to be a related issue causing the warnig.
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.
I cannot confirm that warning with valgrind, It seems to be a false positive.
QElapsedTimer::clockType() seems to be not a nop() in some cases. Can we move the check to an extra function and call it only once? In my case "QElapsedTimer::ClockType::MonotonicClock" is returned. QElapsedTimer::ClockType::PerformanceCounter" is nerver returned. |
Checking clock time make not much sense. The documentation says: "QElapsedTimer will always use the same clock type in a particular machine, so this value will not change". It just tells you which implementation is used on the particular system (result will differ on operating system). |
I agree. Since Qt uses the timer internternally for the same propose we, may rely on Qt that they will use the optimal timer. Since system calls are involve, a subject to change with OS versions, we may even consider to check the monotonic nature it in release builds. So the check is in place for every "particular machine". |
On any modern desktop computer with any OS officially supported by Mixxx, you will get a monotonic clock. |
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.
Just a final nit pick and than LGTM
src/util/performancetimer.h
Outdated
// TODO: turn this into a static_assert and inline this method | ||
// once Qt enables it. |
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.
This will probably never be a static assertion, because it depends on the OS executing Mixxx.
// TODO: turn this into a static_assert and inline this method | |
// once Qt enables it. |
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.
yes, but the OS / target is usually known at compile time anyways. That's why the stdlib can get a away with a constexpr static is_steady
. https://en.cppreference.com/w/cpp/chrono/system_clock
I'm unsure whether the clock is monotonic in all cases. I experienced some test failures. https://github.com/Swiftb0y/mixxx/actions/runs/5913234283/job/16037422961 |
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.
LGTM, thank you.
you just merged dozens of fixup commits.... how do we proceed? |
@Swiftb0y Switch the repo to "Allow squash merging" exclusively for preventing this in the future? Avoids tons of low-value commits in the repo and ensures that I have seen this strategy in a customer project recently and adopted it immediately after experiencing the benefits. Saves you from a lot of headaches. |
Refactor timers (#11807) 2nd try
Initially wanted to investigate something performance-related but then got distracted by the questionable state of
util/performancetimer.h
andutil/timer.h
so I decided to clean it up a little.