-
Notifications
You must be signed in to change notification settings - Fork 1.5k
introduced a cache for followAllReferences()
calls
#7192
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
base: main
Are you sure you want to change the base?
Conversation
This essentially eliminates any meaningful impact by
Clang 19
The example from https://trac.cppcheck.net/ticket/10765#comment:4: Clang 19
|
lib/valueflow.cpp
Outdated
[&] { | ||
// Follow references | ||
auto refs = followAllReferences(tok); | ||
auto refs = tok->refs(); |
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.
needs to be const auto&
.
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.
Done.
I filed https://trac.cppcheck.net/ticket/13533 about detecting this.
return Action::Invalid; | ||
// Follow references | ||
auto refs = followAllReferences(tok); | ||
auto refs = tok->refs(); |
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 copy is necessary since an additional entry is being added. But I think this is not necessary and I will try to refactor the code to avoid 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.
I adjusted this by unfortunately there is some redundant code introduced.
if (!mImpl->mRefs) | ||
mImpl->mRefs = new SmallVector<ReferenceToken>(followAllReferences(this)); | ||
return *mImpl->mRefs; | ||
} |
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 is not the right way to do this. This is a const
method that is modifying the token. Instead followAllReferences
should be moved to the SymbolDatabase
and there should be a pass that fills this in for all of the tokens.
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. I was about to add a comment about this. This violates the technical const
and if we would not allow this (I hope some day I will finish up that change) this would require mutable
(which from my experience is acceptable for caches inside objects).
I am not sure how easy it would be to implement an earlier pass since it is not done for all tokens but there are lots of checks which are performed before we actually end up following references. That would need to be replicated I reckon - and that also has a certain visible overhead and we would need to run through that twice then.
Actually I would also have the ValueFlow behave this way so we might avoid running it for code which is not relevant.
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 is not the right way to do this. This is a
const
method that is modifying the token.
That should be totally fine (by precedent). We modify const Token
objects all over the place in the ValueFlow and symbol database via const_cast
. Obviously it would be better if we didn't but here it is much cleaner and in a single place and as stated before I think this is acceptable practice.
Actually I would also have the ValueFlow behave this way so we might avoid running it for code which is not relevant.
Please disregard this. This is wishful thinking as this would not be possible the way the ValueFlow is working. I totally forgot I already looked into this.
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 const_cast should be fixed, but we shouldn't add more code that needs to be fixed.
Also this is called in ValueFlowForward and ValueFlowReverse so its already called on almost every token in functionScopes, so it really won't help performance being a cache.
Furthermore, in copcheck we update the tokens through passes rather than using a cache, this makes it easier to debug and we can provide this information to addons later on. So doing a pass in SymbolDatabase would be consistent with the rest of the code.
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.
Furthermore, in copcheck we update the tokens through passes rather than using a cache, this makes it easier to debug and we can provide this information to addons later on. So doing a pass in SymbolDatabase would be consistent with the rest of the code.
Will give it a try and check how it impacts performance.
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.
Do we know if it causes a perf impact or how much? It seems we are making it worse for premature optimizations.
There are other advantages to doing it the correct way too such as better debugging and addons can take advantage of this information (this seems like a useful analysis for addons). So if we enable it for addons then we will beed to run a pass regardless.
Also you could consider skipping this for functions we are skipping analysis for, if the performance is too bad, but it would be good to see some actual numbers to make this decision.
This comment was marked as duplicate.
This comment was marked as duplicate.
Sorry, something went wrong.
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.
So the current approach seems like the best approach.
I meant to say "It seems like the currently best approach".
Do we know if it causes a perf impact or how much? It seems we are making it worse for premature optimizations.
Various performance numbers are in the PR. It is a massive improvement. It would also help with the runtime of the CI.
Also you could consider skipping this for functions we are skipping analysis for, if the performance is too bad, but it would be good to see some actual numbers to make this decision.
That was an idea regarding the ValueFlow (see https://trac.cppcheck.net/ticket/12528) but that won't work since not all passes are based on function scopes. But that is currently out-of-scope and is something I am looking at within another context hopefully soon.
It might actually not an issue after all because with the duplicated calls eliminated it basically no longer has any footprint. The only issue could be that we perform it for more tokens than we actually need so that would introduce new overhead but it might not be much. Will test that. Although I would prefer not to have that at all since all the overhead adds up - a lot.
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 just realized this is called when setting exprids, so it always called on every token regardless of ValueFlow analysis.
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 just gave it a small spin and it does not increase the Ir so that looks feasible.
void setCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint value); | ||
bool getCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint &value) const; | ||
|
||
SmallVector<ReferenceToken>* mRefs{}; |
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.
If this is going to be a pointer, you should use std::unique_ptr
or std::shared_ptr
.
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 modeled it after mValues
which is also just a raw pointer.
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.
You should model it after mAttributeAlignas
, thats a much safer choice. I think the raw pointers were used before managed pointers were available.
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.
That requires ReferenceToken
to be a complete type and the astutils.h
to be added.
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.
You can just move the ReferenceToken
to the token.h header.
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.
True, but followAllReferences()
is the code which produces the ReferenceToken
objects so I would prefer to keep those together.
It is unfortunate enough that smallvector.h
spilled into more code.
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 spillage could be addressed by moving TokenImpl
into the implementation (hence the name). I tried that in the past but unfortunately the code relies on the implementation being exposed - I will give it another try.
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.
Onward into the next rabbithole: #7831.
Something really weird is going on here in the UBSAN job:
The timing information for |
Before
After
|
42d1ec9
to
b38e8ba
Compare
b38e8ba
to
a36380e
Compare
Could we merge this without the debug integration for now (I will file a ticket about doing that)? I would like to clean up and test the existing debug output first (see #7258 - as usual stalled) before I add new data. And it would greatly speed up the CI as well as giving a baseline to compare against if running it as a pass would have significant performance impact. |
a36380e
to
f54680c
Compare
This should be done in the symboldatabase either before or during exprids. |
lib/vf_analyzers.cpp
Outdated
return a; | ||
} | ||
if (analyzeTok) { | ||
auto a = analyze_f(tok); |
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 dont really like this at all, it is harder to follow and harder to extend in the future.
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 dropped it for now.
bool inconclusive = true, | ||
ErrorPath errors = ErrorPath{}, | ||
int depth = 20); | ||
SmallVector<ReferenceToken> followAllReferences(const Token* tok, bool temporary = 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.
Since we are removing the inconclusive
flag, then we should add that to the ReferenceToken
so we can know if it was inconclusive or not.
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 removed flags were not used outside of astutils.cpp
itself. If that information is need in the future we can make the suggested change.
lib/token.cpp
Outdated
return mTokensFrontBack.list.getFiles()[mImpl->mFileIndex]; | ||
} | ||
|
||
const SmallVector<ReferenceToken>& Token::refs() const |
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 is missing the bool temporary
parameter.
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.
That was intentional - added.
Thanks for the feedback. Some of the shortcomings were intentional since it seemed to make things simpler and I wanted to get the performance improvement in. But I remembered the changes to be simpler which is not the case so I need a bit to get into it again. |
@pfultz2 How should the information be presented in the debug output? I also assume it should be a separate section. |
fb74dde
to
6699af7
Compare
In the limited cases I tested that produced considerably more calls to |
followAllReferences()
calls with default parametersfollowAllReferences()
calls
Given the improvement this change provides and how it affects the CI (we are actually getting even slower and not having this applied makes it harder to pinpoint the other hot spots) I would really prefer if we could just merge the very first version and do the adjustments as a follow-ups. |
Having this done in incremental commits would also make it possible to bisect differences in behavior/performance this might have based on the stage this cache is generated. Especially after I tried to add a different cache in #7573 which relies on that the results are not being cached beyond a point (which might also cause issues here if this is stored too early - but might not be reflected in our tests). |
1031202
to
92ad6f3
Compare
I dropped the changes to generate these beforehand as it was too unspecific. The performance gains are just too big to delay this any longer. I will file a ticket and publish a PR with the WIP code later. And I would really prefer that to land it in separate commits anyways so it will make it easier to distinguish possible issues caused by adding the cache and by precomputation of the cache. I am not also confident the data might actually correct when precomputated after working on #7573 (I am trying to improving guardrails so we could detect that). I also confirmed that adding the cache shows no differences with #7800. |
92ad6f3
to
90c3d46
Compare
|
No description provided.