This is an archive of the discontinued LLVM Phabricator instance.

[clang-format] Fixed bad performance with enabled qualifier fixer.
ClosedPublic

Authored by Sedeniono on Jun 18 2023, 8:40 AM.

Details

Summary

This fixes github issue #57117: If the "QualifierAlignment"
option of clang-format is set to anything else but "Leave", the
"QualifierAlignmentFixer" pass gets enabled. This pass scales
quadratically with the number of preprocessor branches, i.e.
with the number of elements in TokenAnalyzer::UnwrappedLines.
The reason is that QualifierAlignmentFixer::process() generates
the UnwrappedLines, but then QualifierAlignmentFixer::analyze()
calls LeftRightQualifierAlignmentFixer::process() several times
(once for each qualifier) which again each time generates the
UnwrappedLines.

This commit gets rid of this double loop by registering the
individual LeftRightQualifierAlignmentFixer passes directly in
the top most container of passes (local variable "Passes" in
reformat()).
With this change, the original example in the github issue #57117
now takes only around 3s instead of >300s to format.

Since QualifierAlignmentFixer::analyze() got deleted, we also
no longer have the code with the NonNoOpFixes. This causes
replacements that end up not changing anything to appear in the
list of final replacements. There is a unit test to check that
this does not happen: QualifierFixerTest.NoOpQualifierReplacements.
However, it got broken at some point in time. So this commit
fixes the test. To keep the behavior that no no-op replacements
should appear from the qualifier fixer, the corresponding code
from QualifierAlignmentFixer::analyze() was moved to the top
reformat() function. Thus, is now done for every replacement
of every formatting pass. If no-op replacements are a problem
for the qualifier fixer, then it seems to be a good idea to
filter them out always.

See
https://github.com/llvm/llvm-project/issues/57117#issuecomment-1546716934
for some more details.

Diff Detail

Event Timeline

Sedeniono created this revision.Jun 18 2023, 8:40 AM
Herald added projects: Restricted Project, Restricted Project, Restricted Project. · View Herald TranscriptJun 18 2023, 8:40 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
Sedeniono requested review of this revision.Jun 18 2023, 8:40 AM

I'm shocked, astonished and in awe how small this patch is. Great work and explanation!

clang/lib/Format/Format.cpp
3475–3476

Just increase here, and add a comment that there are multiple passes added in addQualifierAlignmentFixerPasses.

I think the idea was and should be that we always store the pointers on the stack.

clang/lib/Format/QualifierAlignmentFixer.cpp
36

I think we can drop this, see my other comment.

As suggested by HazardyKnusperkeks, increased the number of stack elements and removed the reserve(). That causes all Passes to be stored on the stack. (I counted 17 distinct possible passes, but not all of them can be active at the same time: Some are javascript and some a C++ exclusive.)

Sedeniono marked 2 inline comments as done.Jun 19 2023, 11:37 AM
Sedeniono added inline comments.
clang/lib/Format/Format.cpp
3475–3476

Ok, done.

Please wait for at least @MyDeveloperDay to say something, as he is the original author of the work.

clang/lib/Format/Format.cpp
3475–3476

You don't need to comment done. But please check the "Done" on my comment. :)

This revision is now accepted and ready to land.Jun 19 2023, 1:00 PM
owenpan added inline comments.Jun 21 2023, 5:23 PM
clang/lib/Format/Format.cpp
3476–3477

This comment is unnecessary and too verbose IMO. I suggest that it be deleted.

3587–3588

You only need this for the QualifierAlignment passes as others (e.g. IntegerLiteralSeparatorFixer) already skip no-op replacements.

clang/lib/Format/QualifierAlignmentFixer.cpp
29

Ditto.

clang/lib/Format/QualifierAlignmentFixer.h
29
MyDeveloperDay accepted this revision.Jun 21 2023, 10:17 PM

Thank you for the detailed analysis

Sedeniono marked an inline comment as done.Jun 22 2023, 9:23 AM
Sedeniono added inline comments.
clang/lib/Format/Format.cpp
3587–3588

Yes, in principle I need to do this only after all QualifierAlignment passes ran. The key point is that it needs to be done only after all QualifierAlignment passes ran. In other words, I cannot move this to LeftRightQualifierAlignmentFixer::analyze(); it would do nothing there. The non-no-op code exists so that if e.g. const volatile becomes volatile const in one pass and then again const volatile in another pass, we end up with no replacements.

I also cannot simply put all the QualifierAlignment passes into a simple dedicated loop that directly runs them after one another; the part with applyAllReplacements() etc (line 3554 till 3566 above) would be missing between the passes. I could, of course, extract lines 3554 till 3566 into a new function and call it in both places. Should I do that?

Or do you mean that I should wrap the NonNoOpFixes code in an if (Style.QualifierAlignment != FormatStyle::QAS_Leave)?

clang/lib/Format/Format.cpp
3476–3477

This comment is unnecessary and too verbose IMO. I suggest that it be deleted.

I think one should note that there are more passes added than directly meets the eye here.

owenpan added inline comments.Jun 23 2023, 1:34 AM
clang/lib/Format/Format.cpp
3587–3588

Or do you mean that I should wrap the NonNoOpFixes code in an if (Style.QualifierAlignment != FormatStyle::QAS_Leave)?

I was thinking about something like that at the minimum.

As suggested by the reviewers, the noop fixes are now filtered only if the qualifier alignment passes are active. Also implemented the other minor refactorings.

Sedeniono marked 6 inline comments as done.Jun 23 2023, 11:06 AM
Sedeniono added inline comments.
clang/lib/Format/Format.cpp
3476–3477

After looking at the code again, I decided to remove the comment. I think the "structure" of the code below that adds the passes already highlights that there is something "special" going on with the qualifier alignment passes, since they aren't added via a lambda.

owenpan accepted this revision.Jun 23 2023, 7:18 PM

For us to land this for you we'll need your name and email

Sedeniono marked an inline comment as done.Jun 24 2023, 11:54 PM

For us to land this for you we'll need your name and email

Please use: Sedenion <39583823+Sedeniono@users.noreply.github.com>
Thanks!