MDEV-35770 Augment DBUG_ASSERT with __builtin_assume#3944
MDEV-35770 Augment DBUG_ASSERT with __builtin_assume#3944UltraGreed wants to merge 1 commit intoMariaDB:mainfrom
Conversation
|
Hi @UltraGreed! What is the intention of this pull request? Why is it submitted under MDEV-35770 umbrella? What kind of feedback are you expecting from MariaDB developers? |
|
@FooBarrior is mentoring this effort. |
|
Hello, @svoj. Sorry for the delayed response. I've updated the PR to better reflect its intention. The only commit uploaded so far is a small fix (related to described in the PR problem), which will be squashed into the main commit later. |
e996848 to
707a7c4
Compare
a2689a9 to
6e2600f
Compare
|
Reset draft status as the ball is in mentor's hands. |
e2a6bba to
f74948b
Compare
FooBarrior
left a comment
There was a problem hiding this comment.
I have made the (minor) changes I wanted, now this looks good to me.
@svoj please also take a look as a second reviewer
|
as a good example where it clearly has an effect, I would provide There we could see, that not only clang 21 does a loop inversion, but also a partial loop unrolling, even under -O2 |
|
please take a look on Windows optimized build. CI won't block that for you, on whatever reason, but appveyor does actually complain. |
6e61b91 to
c832b8d
Compare
|
@vaintroub can you tell something about appveyor results now -- is it somehing common, or is it this PR's issue? |
|
If this does not happen elsewhere, e.g in the base branch for this one, but happens here, then it is PR issue. virtual ~Sql_cmd()
{
/*
Sql_cmd objects are allocated in thd->mem_root.
In MySQL, the C++ destructor is never called, the underlying MEM_ROOT is
simply destroyed instead.
Do not rely on the destructor for any cleanup.
*/
DBUG_ASSERT(false);
} |
Should this be rewritten as something like |
yes, and perhaps remove |
|
Hmm I guess we can't do it @svoj. However adding derivation breaks it: |
|
So our best bet as i see it is to make those assertions _NO_ASSUME. It seems that C++ requires a reachable destructor (even though we know it will not be reachable,). |
e5696b5 to
32346c4
Compare
|
A few concerns from my side, mostly inline with assert != assume whitepaper shared by @FooBarrior.
Unless there's a clear performance improvement, I'd definitely vote against "assert is assume" approach. I'd even be very cautious with "assume is assert" approach advertised by the whitepaper: too easy to misuse. But the good thing is that now we know places where |
|
I generally disagree with the assert != assume whitepaper, I think they use asserts incorrectly, and think that assert = assume. Still, the point on whether it will actually improve the performance is valid. If it doesn't help, it's just useless code change that only makes it more complex. |
|
@vuvova we have ~10000 A few examples... debug assert = debug assume, but debug assert != release assume, because it depends on some DBUG specific code. Somewhat unrelated, but still... here we don't want to assume mutex is locked, we want to terminate execution. It generally applies to all We may want to interrupt debug builds on incorrect inputs, but let release builds handle it. We don't want this handling to be optimised away. This one wouldn't be correct assumption either, we want to interrupt here: Yes, these can be incorrect |
Here only case 1 is a valid reason not to assume asserts |
gkodinov
left a comment
There was a problem hiding this comment.
This is a preliminary review. Can you please address the comments by the reviewer? There's also failing build tests that need to be fixed.
Remove runtime-check after assert, for which there is no knowledge that it was executed in 10 years. Ignore "assume has side-effect" warning (-wassume) for Clang. Also call DBUG_ASSERT in DBUG_ASSERT_NO_ASSUME in DBUG_ASSERT_AS_PRINTF
There was a problem hiding this comment.
I have played a bit with the diff: merged it, cleaned it up etc.
The only thing failing is the clang memsan build atm. And it's failing massively. I didn't prove it, but my guess is that it's now checking something that depends on the data structures being set to 0 in debug mode and not initialized otherwise. Or there's some side effect of the function calls in DBUG_ASSERT().
I would join @svoj in requesting a benchmark.
I've grep-ed through the DBUG_ASSERTs: there's about 1.5k of these. A serious amount of them are calling functions. Some of them quite heavy. So having these in assume() I believe doesn't contribute to the performance speedup, but further slows the run in optimized mode since it now needs to evaluate these functions. Also a good % of the ASSERTs do not take advantage of assuming the things that are asserted: most of them are double-checking state of complex data structures before executing an action on them.
So, while I understand and can relate to the basic premise of the PR, I believe that we need to take a different approach: instead of doing that to all DBUG_ASSERTs by default we need to have a DBUG_ASSERT_ASSUME() instead, then evaluate each case where DBUG_ASSERT() is used and replace with _ASSUME() if there's going to be benefits.
IMHO, to have benefit from the assert_assume, the DBUG_ASSERT should:
- not be calling any functions
- there should be a similar condition right after the DBUG_ASSERT() or close to it.
- ideally the DBUG_ASSERT() should just be checking a single condition over a single variable.
- The expression should reference a variable. constant expressions are not worth it.
Description
The DBUG_ASSERT macro tests an invariant at runtime in debug build and aborts if the check fails. Although assertions are disabled in optimized builds, code is usually written in such a manner, that failing an assertion condition would lead to undefined behavior. It therefore makes sense to introduce platform-specific analog of C++23
[[assume(cond)]]and use it in these cases, which potentially could lead to generation of more optimized builds in production.E.g. compiler would be able to optimize
whileloop intodo-whileloop (which means less instructions in assembly) if it knows that the loop will run at least once.Special patterns case
There are lots of code fragments like:
and
If we simply wrap DBUG_ASSERT in an assume, the compiler will elide both the if-guard and the default branch — removing important fallback code.
There are also cases like:
In this case compilation in release mode would fail, because assume is called with undeclared variable.
This PR:
MY_ASSUME is implemented for GCC >= 13.0, Clang and MSVC.
TODO: Measure actual performance improvements and document LLVM/assembly diffs.-- will stay out of scope for this PRHow can this PR be tested?
This patch doesn't intend to change the behaviour, so it's only important that the existing tests will pass. We assume that test coverage of the special cases is out of scope for this work -- it was supposed to have been done in scope of the same task when it was added, if that was feasible at all.
We acknowledge we may have missed a few “special” patterns, but we’ve covered all the occurrences we could find.
Basing the PR against the correct MariaDB version
mainbranch.PR quality check