-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix #12861 Hang in valueFlowCondition() with huge array #7757
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
Conversation
|
Please add a test in |
The test already exists and it is the one failing with this change applied. So somehow this actually makes things worse. |
|
Yes, I will check it again. If I can't fix it, I will ignore this PR. |
|
I updated the commit. For the test case submitted by the ticket #12861, the performance is improved a lot, at least the consumed time can be reduced to less than 2 seconds. For other test cases, I don't have the exact testing data, but I think this can be helpful for the performance. |
|
Is there a reason not to implement caching in the regular |
|
Hi CHR, the process of creating the AST tree is a little complicated for me currently, and I can't figure out the details, that is also why I didn't modify the createAst(). As I understand, during TokenList::createAst(), the astTop() is used. But at that time, the createAst() has not been finished, so the top is a temporary one, which can not be cached as the final top. So we need two versions of the function, one is for creating ast, and another with the cache function is for the usage after the ast is created. |
|
I think this sounds reasonable: |
7032692 to
ca60f59
Compare
lib/token.h
Outdated
| #include "templatesimplifier.h" | ||
| #include "utils.h" | ||
| #include "vfvalue.h" | ||
| #include "tokenlist.h" |
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 seems unnecessary.
lib/token.h
Outdated
|
|
||
| } | ||
| RET_NONNULL Token *astTop() { | ||
| /** If the ast tree has not been created, pls make sure to use cache=false, |
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.
remove the "pls" :-)
|
|
There is a less intrusive way to address this by exiting early - see #7822. |
|
I do think the function ‘Token *astTop()’ should be updated. It is very apparent that if we can figoure out the top of a token by parsing an AST tree, there is no need to loop the parents each time we getting the top, and we should just return the top. |
lib/token.h
Outdated
| } | ||
| while (ret->mImpl->mAstParent) | ||
| ret = ret->mImpl->mAstParent; | ||
| mImpl->mAstTop = ret; |
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.
We shouldn't update the mAstTop in the astTop getter. There should be a astTop(Token* tok) method(similar to astParent setter) to set the top and you can set them in createAst after the loop is finished(so there wont be any new ast updates).
This can also be done with a more efficient algorithm as well as you can start at the top node and traverse down, whereas setting it in the getter requires it to traverse up multiple times for the same top node.
lib/tokenlist.cpp
Outdated
| Token * top = init; | ||
| while (top->astParent()) { | ||
| top = top->astParent(); | ||
| } |
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.
By moving the setting of mAstTop out of the getter, then you dont need to copy and paste the astTop loop everywhere.
|
As per my previous comment these changes are not necessary at all - see #7822. I will give that a small run with |
I think precalculating astTop is a more robust solution. #7822 relies on probability that |
|
Updated the commit. I am not familiar with details about how the ast tree is created. So I set the tops for the tokens at the time after the tree is created instead of during that process. That means the tree is parsed again. Even though, the benefit I think is good enough. The time of running the case in ticket 12861 is less than 1 second. And I simply recorded the time of running the cppcheck/testrunner. It is improved a lot. Anyway, with resolving the astTops problem, the performance I think can be improved a lot. |
lib/tokenlist.cpp
Outdated
| Token * top = tok; | ||
| while (top->astParent()) { | ||
| top = top->astParent(); | ||
| } |
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.
There is no need to traverse up. We only need to traverse down from the top nodes(which are nodes that dont have a parent but have children):
for (Token *tok = mTokensFrontBack->front; tok; tok = tok ? tok->next() : nullptr) {
if(tok->astParent())
continue;
if(!tok->astOperand1() && !tok->astOperand2())
continue;
visitAstNodes(tok, [&](Token* child) {
child->astTop(tok);
return ChildrenToVisit::op1_and_op2;
});
}
lib/token.cpp
Outdated
| tok = tok->astTop(); | ||
| while (tok->mImpl->mAstParent) { | ||
| tok = tok->mImpl->mAstParent; | ||
| } |
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 should just use astTop since it falls back to the loop when the top is not set.
lib/token.h
Outdated
| */ | ||
| void astParent(Token* tok); | ||
| void astTop(Token * tok) { | ||
| if (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.
Dont check for null, we should be able to use this to clear the top if we want to.
lib/tokenlist.cpp
Outdated
| top = top->astParent(); | ||
| } | ||
| semicolon1->astOperand1(top); | ||
| } |
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 change should be reverted, it cam be written as semicolon1->astOperand1(init->astTop()) as astTop can still be called before it stores the top.
04eb081 to
379f0aa
Compare
|
|
Thanks for the comments! The commit is updated. There is a scriptcheck failing which seems not the submitted code problem. |
|
What is the performance impact? Is it possible to reduce the timeout of the existing test? |
|
Yes. I tested the case posted on the ticket 12861 (https://trac.cppcheck.net/ticket/12861), the consumed time can be reduced to less than 1 second. |
Ok, so please reduce the timeout for the existing test in |
|
Sorry, I am a little confused about the requirement. I run the test/cli/performance_test.py locally, and this commit passed the tests. Do you mean we need to add one more case to test/cli/performance_test.py? If so, how to set the case and the expected result? Can I completely copy the case posted on the ticket 12861 directly? |
|
See here: cppcheck/test/cli/performance_test.py Line 225 in 574fffa
That's the code from https://trac.cppcheck.net/ticket/12861 with an added namespace. Please reduce the timeout appropriately. The test should now also run on MacOS. |
a5ce2f9 to
aff2cca
Compare
|
I have done some experiments. The existing test (array in namespace) shows no improvement by this change (~8.8 s on my system). So the timeout should stay the same. On the other hand, the code from #12861 (array in function) is now checked in 11 s vs. practically a hang without the change. |
|
I adjusted the timeout value set by the code line: I reduced the value from 20 to 15. |
Ok, I just saw this comment. I will check this later. |
Please refer to my previous comment #7757 (comment) |
eae44c5 to
5261c77
Compare
5261c77 to
c3b1b6a
Compare
|



I tested the fix based on 4617bc2.
Use the cppcheck to check the below code which is submitted on the ticket #12861.
With the fix, the overall time is 1.52582s.
Without the fix, the time is 25.9674s.
I also tested the testrunner for running the whole of the unit tests. There is some performance improment with the fix, but not remarkable.