Skip to content
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

llvm-cov export spends most of its time in skipOtherFiles #62079

Closed
jonhoo opened this issue Apr 12, 2023 · 3 comments · Fixed by #122050
Closed

llvm-cov export spends most of its time in skipOtherFiles #62079

jonhoo opened this issue Apr 12, 2023 · 3 comments · Fixed by #122050

Comments

@jonhoo
Copy link

jonhoo commented Apr 12, 2023

As part of digging into a performance issue in a tool built on top of llvm-cov (specifically, it calls llvm-cov export), I realized that llvm-cov export was taking upwards of 4s to run on a modestly-sized input. That is, it takes 4s to complete this invocation:

llvm-cov export some-binary --instr-profile some.profdata --format lcov > /dev/null

where some-binary is a Rust binary of about 370M and some.profdata is 4M. And, during this time, llvm-cov appears to be saturating a single core on my multi-core host.

I built llvm-cov from the source in main and ran it through perf and Valgrind, and an interesting picture emerged (Firefox profiler visualization). llvm-cov appears to be spending a majority of its time in

void FunctionRecordIterator::skipOtherFiles() {
while (Current != Records.end() && !Filename.empty() &&
Filename != Current->Filenames[0])
++Current;
if (Current == Records.end())
*this = FunctionRecordIterator();
}

The calls to that function appear to stem from this call in renderFile:

if (!ExportSummaryOnly && !SkipFunctions) {
renderFunctions(OS, Coverage.getCoveredFunctions(Filename));
}

Which is ultimately called from CoverageExporterLcov::renderRoot via renderFiles.

Valgrind appears to struggle to follow the full call-graph, but from what I can see, it appears that skipOtherFiles is called upwards of 650k times. It also appears to be the case that renderRoot is itself called 300k times (possibly by itself — the call graph is unclear). In other words, it's not clear that skipOtherFiles itself is the problem (although it is doing a linear search as part of an iterator if I read it correctly?); the problem may be in the number of times it gets called. Perhaps some caching of its results are warranted?

My lack of LLVM knowledge is preventing me from digging much further into this, but I'd be happy to run additional commands/instrument llvm-cov in whatever ways may be helpful to surface more information to track down the underlying issue.

In case it matters, I'm running on an AArch64 host.

@jonhoo
Copy link
Author

jonhoo commented Apr 12, 2023

cc @bcardosolopes perhaps who has done some performance optimization work on llvm-cov in the past.

@jonhoo
Copy link
Author

jonhoo commented Apr 24, 2023

cc @tunz and @vedantk who also landed llvm-cov performance improvements previously

EDIT: Oh, and perhaps @mysterymath since you seem to be the person who's generally contributing to coverage work these days.

@glandium
Copy link
Contributor

glandium commented Jan 8, 2025

I have some work in progress that brings down a llvm-cov run on Firefox's libxul.so with a 2.5MB profile from 13 minutes down to 19 seconds. I'll open a PR when I'm 100% confident it doesn't break anything.

glandium added a commit to glandium/llvm-project that referenced this issue Jan 8, 2025
When iterating over function records, filtered by file name, currently,
the iteration goes over all the function records, repeatedly for each
source file, essentially giving quadratic behavior.

413647d sped up some cases by keeping
track of the indices of the function records corresponding to each
file name. This change expands the use of that map to
FunctionRecordIterator.

On a test case with Firefox's libxul.so and a 2.5MB profile, this brings
down the runtime of `llvm-cov export $lib --instr-profile $prof -t lcov`
from 12 minutes with 90% spent in skipOtherFiles to 19 seconds with no
samples in skipOtherFiles at all under a sampling profiler (with a
sampling interval of 1ms).

Fixes llvm#62079
glandium added a commit to glandium/llvm-project that referenced this issue Jan 8, 2025
When iterating over function records, filtered by file name, currently,
the iteration goes over all the function records, repeatedly for each
source file, essentially giving quadratic behavior.

413647d sped up some cases by keeping
track of the indices of the function records corresponding to each
file name. This change expands the use of that map to
FunctionRecordIterator.

On a test case with Firefox's libxul.so and a 2.5MB profile, this brings
down the runtime of `llvm-cov export $lib --instr-profile $prof -t lcov`
from 12 minutes with 90% spent in skipOtherFiles to 19 seconds with no
samples in skipOtherFiles at all under a sampling profiler (with a
sampling interval of 1ms).

Fixes llvm#62079
glandium added a commit to glandium/llvm-project that referenced this issue Jan 15, 2025
When iterating over function records, filtered by file name, currently,
the iteration goes over all the function records, repeatedly for each
source file, essentially giving quadratic behavior.

413647d sped up some cases by keeping
track of the indices of the function records corresponding to each
file name. This change expands the use of that map to
FunctionRecordIterator.

On a test case with Firefox's libxul.so and a 2.5MB profile, this brings
down the runtime of `llvm-cov export $lib --instr-profile $prof -t lcov`
from 12 minutes with 90% spent in skipOtherFiles to 19 seconds with no
samples in skipOtherFiles at all under a sampling profiler (with a
sampling interval of 1ms).

Fixes llvm#62079
DKLoehr pushed a commit to DKLoehr/llvm-project that referenced this issue Jan 17, 2025
When iterating over function records, filtered by file name, currently,
the iteration goes over all the function records, repeatedly for each
source file, essentially giving quadratic behavior.

413647d sped up some cases by keeping
track of the indices of the function records corresponding to each file
name. This change expands the use of that map to FunctionRecordIterator.

On a test case with Firefox's libxul.so and a 2.5MB profile, this brings
down the runtime of `llvm-cov export $lib --instr-profile $prof -t lcov`
from 12 minutes with 90% spent in skipOtherFiles to 19 seconds with no
samples in skipOtherFiles at all under a sampling profiler (with a
sampling interval of 1ms).

Fixes llvm#62079
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants