Skip to content

ProgramMemory: avoid unnecessary copy in erase_if()#6652

Draft
firewave wants to merge 1 commit intocppcheck-opensource:mainfrom
firewave:pm-const-2
Draft

ProgramMemory: avoid unnecessary copy in erase_if()#6652
firewave wants to merge 1 commit intocppcheck-opensource:mainfrom
firewave:pm-const-2

Conversation

@firewave
Copy link
Copy Markdown
Collaborator

No description provided.

@firewave
Copy link
Copy Markdown
Collaborator Author

-D__GNUC__ --check-level=exhaustive ../lib/utils.cpp

Clang 17 773,369,412 -> 772,984,251
GCC 14 794,493,015 -> 795,010,529

The example from https://trac.cppcheck.net/ticket/10765#comment:4:

Clang 17 3,988,168,871 -> 3,989,222,640
GCC 14 4,098,327,498 -> 4,100,109,857

I thought this improves things slightly. Seems like I misread the local results I had before. But maybe it will improve things if the use count thing is fixed...

Comment thread lib/programmemory.cpp Outdated
continue;

copyOnWrite();
mValues->erase(it->first);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This requires double iteration for ever element that will be erased.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yeah - the code isn't great. I mainly opened this to raise awareness and gather feedback for a better implementation (if even possible).

Comment thread lib/programmemory.cpp Outdated
it = mValues->erase(it);
else
++it;
for (auto it = values->begin(); it != values->end(); ++it) {
Copy link
Copy Markdown
Contributor

@pfultz2 pfultz2 Aug 1, 2024

Choose a reason for hiding this comment

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

The initial value should come from find_if:

for (auto it = std::find_if(mValues->begin(), mValues->end(), [](const auto& x) { return pred(x.first); }); it != mValues->end(); ++it) {
    copyOnWrite();
    if (pred(it->first))
        it = mValues->erase(it);
    else
        ++it;
}

Copy link
Copy Markdown
Contributor

@pfultz2 pfultz2 Aug 1, 2024

Choose a reason for hiding this comment

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

You can move the copyOnWrite out of the loop by doing find_if outside of the for statement:

auto it = std::find_if(mValues->begin(), mValues->end(), [](const auto& x) { return pred(x.first); });
if (it == mValues->end())
    return;
copyOnWrite();
for (; it != values->end(); ++it) {
    if (pred(it->first))
        it = mValues->erase(it);
    else
        ++it;
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The container behind mValues changes which renders the iterators invalid.

Comment thread lib/programmemory.cpp
else
++it;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I dont think this extra branch is needed. Especially if you can move the copyOnWrite out of the loop.

@firewave
Copy link
Copy Markdown
Collaborator Author

The latest rework get rid of the unnecessary copy-on-writes and tries to minimizes the redundant work. Unfortunately this is not really faster at all with few conditions - needs some more looking at with more complex code though.

It exposed an issue with std::find_if though with the unexpected copies of objects which needs to be looked into.

@firewave
Copy link
Copy Markdown
Collaborator Author

It exposed an issue with std::find_if though with the unexpected copies of objects which needs to be looked into.

    auto it = std::find_if(mValues->cbegin(), mValues->cend(), [&pred](const decltype(mValues->cbegin())::value_type& entry) {
        return pred(entry.first);
    });

The actual type provided by this is std::pair<const ExprIdToken, ValueFlow::Value>. The missing const on the first type is causing the copies.

@firewave
Copy link
Copy Markdown
Collaborator Author

The actual type provided by this is std::pair<const ExprIdToken, ValueFlow::Value>. The missing const on the first type is causing the copies.

I filed an upstream ticket about detecting this: llvm/llvm-project#104572. Still need to file one about detecting it ourselves.

@firewave firewave force-pushed the pm-const-2 branch 2 times, most recently from 5db44a9 to 47dc554 Compare December 28, 2024 13:46
@firewave
Copy link
Copy Markdown
Collaborator Author

This greatly reduces the amount of copies but in the used test cases it does not translate into any visible improvements. Let's see what the selfcheck says.

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.

2 participants