Skip to content

Commit e899930

Browse files
authored
[Coverage] Speed up function record iteration (llvm#122050)
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
1 parent 606d0a7 commit e899930

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -748,10 +748,15 @@ struct FunctionRecord {
748748
};
749749

750750
/// Iterator over Functions, optionally filtered to a single file.
751+
/// When filtering to a single file, the iterator requires a list of potential
752+
/// indices where to find the desired records to avoid quadratic behavior when
753+
/// repeatedly iterating over functions from different files.
751754
class FunctionRecordIterator
752755
: public iterator_facade_base<FunctionRecordIterator,
753756
std::forward_iterator_tag, FunctionRecord> {
754757
ArrayRef<FunctionRecord> Records;
758+
ArrayRef<unsigned> RecordIndices;
759+
ArrayRef<unsigned>::iterator CurrentIndex;
755760
ArrayRef<FunctionRecord>::iterator Current;
756761
StringRef Filename;
757762

@@ -760,8 +765,17 @@ class FunctionRecordIterator
760765

761766
public:
762767
FunctionRecordIterator(ArrayRef<FunctionRecord> Records_,
763-
StringRef Filename = "")
764-
: Records(Records_), Current(Records.begin()), Filename(Filename) {
768+
StringRef Filename = "",
769+
ArrayRef<unsigned> RecordIndices_ = {})
770+
: Records(Records_), RecordIndices(RecordIndices_),
771+
CurrentIndex(RecordIndices.begin()),
772+
// If `RecordIndices` is provided, we can skip directly to the first
773+
// index it provides.
774+
Current(CurrentIndex == RecordIndices.end() ? Records.begin()
775+
: &Records[*CurrentIndex]),
776+
Filename(Filename) {
777+
assert(Filename.empty() == RecordIndices_.empty() &&
778+
"If `Filename` is specified, `RecordIndices` must also be provided");
765779
skipOtherFiles();
766780
}
767781

@@ -774,11 +788,29 @@ class FunctionRecordIterator
774788
const FunctionRecord &operator*() const { return *Current; }
775789

776790
FunctionRecordIterator &operator++() {
777-
assert(Current != Records.end() && "incremented past end");
778-
++Current;
791+
advanceOne();
779792
skipOtherFiles();
780793
return *this;
781794
}
795+
796+
private:
797+
void advanceOne() {
798+
if (RecordIndices.empty()) {
799+
// Iteration over all entries, advance in the list of records.
800+
assert(Current != Records.end() && "incremented past end");
801+
++Current;
802+
} else {
803+
// Iterator over entries filtered by file name. Advance in the list of
804+
// indices, and adjust the cursor in the list of records accordingly.
805+
assert(CurrentIndex != RecordIndices.end() && "incremented past end");
806+
++CurrentIndex;
807+
if (CurrentIndex == RecordIndices.end()) {
808+
Current = Records.end();
809+
} else {
810+
Current = &Records[*CurrentIndex];
811+
}
812+
}
813+
}
782814
};
783815

784816
/// Coverage information for a macro expansion or #included file.
@@ -1037,8 +1069,10 @@ class CoverageMapping {
10371069
/// Gets all of the functions in a particular file.
10381070
iterator_range<FunctionRecordIterator>
10391071
getCoveredFunctions(StringRef Filename) const {
1040-
return make_range(FunctionRecordIterator(Functions, Filename),
1041-
FunctionRecordIterator());
1072+
return make_range(
1073+
FunctionRecordIterator(Functions, Filename,
1074+
getImpreciseRecordIndicesForFilename(Filename)),
1075+
FunctionRecordIterator());
10421076
}
10431077

10441078
/// Get the list of function instantiation groups in a particular file.

llvm/lib/ProfileData/Coverage/CoverageMapping.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ unsigned CounterMappingContext::getMaxCounterID(const Counter &C) const {
618618
void FunctionRecordIterator::skipOtherFiles() {
619619
while (Current != Records.end() && !Filename.empty() &&
620620
Filename != Current->Filenames[0])
621-
++Current;
621+
advanceOne();
622622
if (Current == Records.end())
623623
*this = FunctionRecordIterator();
624624
}

0 commit comments

Comments
 (0)