[Development] HEADS-UP: Q_UNLIKELY
Mutz, Marc
marc at kdab.com
Fri Aug 30 15:31:29 CEST 2019
Hi,
Over the years, compilers (at least GCC) have started to interpret
Q_UNLIKELY not only as a way to encode information for the branch
predictor, but more and more to move code marked as such into a
completely different (.cold) ELF section, which is where the exception
tables and stack unwinding code also goes. This means two things:
1. Such code is placed far away from the non-unlikely code, which means
that the normal flow of execution can be more compact, which results in
an increased effective I-cache size (and, presumably, faster execution).
This we all expect from Q_UNLIKELY and Q_LIKELY.
2. As a separate ELF section, barring relocations etc that might prevent
this, these .cold sections will stay parked on disk and loaded into
memory only on demand, which means that executing unlikely code may make
the program actually very slow (like exceptions, which on modern ABIs
have zero runtime overhead when not thrown but may load debug symbols to
unwind the stack when thrown).
This is not something we can control (that I know of, other than telling
Ville to change it), so we don't need to discuss whether that's good or
bad. It simply _is_.
In turn, this means that we should no longer use Q_UNLIKELY (I don't
know about Q_LIKELY, but am willing to be enlightened) to mark code that
gets executed 0.01% of the time, but with very high chance _is_ executed
in normal program flow once, because that causes all the error handling
code to be paged in. We should use it only (but then we should strive to
actually do so) for actual error code. A good indicator is if a
qWarning/qFatal/qCritical is executed: that's error code.
Example: https://codereview.qt-project.org/c/qt/qtbase/+/141519
Q_UNLIKELY however, doesn't work on the following code pattern:
case X: qWarning(...)
if (foo) {
} else {
qWarning(...);
}
if (foo) {
return;
}
qWarning(...);
because it requires an expression that then is assumed to unlikely be
true.
Before C++20, we could reformat the code to invert 'foo' and mark is
Q_UNLIKELY, and the 141519 change did that in some places. C++20 adds
[[unlikely]], which doesn't take a condition, so it can be used in the
above situations. I've proposed it here:
https://codereview.qt-project.org/c/qt/qtbase/+/272244 This also shows
some patterns that at least work with GCC. We also know that
if (foo) [[unlikely]] {}
doesn't work with older GCCs which claim [[unlikely]] support, so we
will need to either add compiler version checks, or yet another macro,
or restrict the patterns to the known-good ones:
Q_UNLIKELY_CASE case-label:
or simply in the flow of execution:
if (foo) {
} else {
Q_UNLIKELY_CASE
qWarning(...);
}
if (foo) {
return;
}
Q_UNLIKELY_CASE
qWarning(...);
The references patch opts for the latter.
Flame away :)
Thanks,
Marc
More information about the Development
mailing list