Skip to content

Fix #15143: Improve performance in the case of many template instantiations - #8960

Open
Oipo wants to merge 2 commits into
cppcheck-opensource:mainfrom
Oipo:main
Open

Oipo wants to merge 2 commits into
cppcheck-opensource:mainfrom
Oipo:main

Conversation

@Oipo

@Oipo Oipo commented Oct 25, 2026

Copy link
Copy Markdown

Two improvements:

  1. CheckClassImpl::virtualDestructor() scanned the whole variable list and the whole token list for every (derived class, public base class) pair. With many template instantiations (for example etl) this took up to 26% of the analysis time.
  2. Tokenizer::setVarIdPass2() tried every out-of-class member definition for every class. matchMemberName() can only succeed when the innermost scope name of the class matches a name in the member's scope, in one of its "using namespace" or in its qualification. The members are now indexed by those names, in their original order, so each class only tries its candidates. The index and matchMemberName() walk the qualification with the same skipQualification().

On my own private project, the total CPU time dropped from 88.7 minutes down to 52.9 minutes. Oh, and this also fixes a false negative: new Derived < int > would never match, and it does now. See some tests I added.

@dmcppcheck

Copy link
Copy Markdown

There should probably be a trac ticket for this PR. Please create it, or ask us to create it. Then the PR title should be updated.

@Oipo

Oipo commented Oct 25, 2026 via email

Copy link
Copy Markdown
Author

@danmar

danmar commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

I have created this ticket:
https://trac.cppcheck.net/ticket/15143

Oipo added 2 commits October 26, 2026 08:29
https://trac.cppcheck.net/ticket/15143

CheckClassImpl::virtualDestructor() scanned the whole variable list and
the whole token list for every (derived class, public base class) pair.
With many template instantiations (for example etl) this took up to 26%
of the analysis time.

When the first base class is checked, the pointer variables of each type
are collected and a single pass over the tokens records, for each pointer
variable, the classes 'Derived' of a "ptr = new Derived" that is followed
by a "delete ptr;". Checking a pair is then a lookup.

This also fixes a false negative: the name of a template instantiation
such as 'Derived < int >' is a single token that contains spaces, so the
old pattern "new Derived < int >" never matched it and the pair was never
reported.

On a project using etl, glaze and Catch2 (211 translation units in two
configurations) total CPU time drops from 88.7 to 61.6 minutes, with
identical output for every translation unit.
https://trac.cppcheck.net/ticket/15143

Tokenizer::setVarIdPass2() tried every out-of-class member definition
for every class. matchMemberName() can only succeed when the innermost
scope name of the class matches a name in the member's scope, in one of
its "using namespace" or in its qualification. The members are now
indexed by those names, in their original order, so each class only
tries its candidates. The index and matchMemberName() walk the
qualification with the same skipQualification().

On the same project total CPU time drops further from 61.6 to 52.9
minutes, with identical output for every translation unit.
@Oipo Oipo changed the title Improve performance in the case of many template instantiations Fix #15143: Improve performance in the case of many template instantiations Oct 26, 2026
@Oipo

Oipo commented Oct 26, 2026

Copy link
Copy Markdown
Author

Thanks. Updated the PR title and the two commits.

Comment thread lib/checkclass.cpp
deletionsCollected = true;

for (const Variable* var : mSymbolDatabase->variableList()) {
if (var && var->isPointer() && var->type())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest a && var->nameToken() also. unnamed parameters will not be added then.

Comment thread lib/checkclass.cpp
{
// pointer variables of type 'Base *'
std::set<int> baseClassPointers;
collectDeletions();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it does not look right to me to call this in the loop. It will go through all variables in symboldatabase and add declaration ids to pointersByType over and over again? can't we just do that once?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants