forked from OSchip/llvm-project
[llvm-cov] Multi-threaded implementation of prepareFileReports method.
Summary: Local testing has demonstrated a great speed improvement, compare the following: 1) Existing version: ``` $ time llvm-cov show -format=html -output-dir=report -instr-profile=... ... The tool has been launched: 00:00:00 Loading coverage data: 00:00:00 Get unique source files: 00:00:33 Creating an index out of the source files: 00:00:34 Going into prepareFileReports: 00:00:34 Going to emit summary information for each file: 00:28:55 <-- 28:21 min! Going to emit links to files with no function: 00:28:55 Launching 32 threads for generating HTML files: 00:28:55 real 37m43.651s user 112m5.540s sys 7m39.872s ``` 2) Multi-threaded version with 32 CPUs: ``` $ time llvm-cov show -format=html -output-dir=report -instr-profile=... ... The tool has been launched: 00:00:00 Loading coverage data: 00:00:00 Get unique source files: 00:00:38 Creating an index out of the source files: 00:00:40 Going into prepareFileReports: 00:00:40 Preparing file reports using 32 threads: 00:00:40 # Creating thread tasks for the following number of files: 16422 Going to emit summary information for each file: 00:01:57 <-- 1:17 min! Going to emit links to files with no function: 00:01:58 Launching 32 threads for generating HTML files: 00:01:58 real 11m2.044s user 134m48.124s sys 7m53.388s ``` Reviewers: vsk, morehouse Reviewed By: vsk Subscribers: Dor1s, llvm-commits, kcc Differential Revision: https://reviews.llvm.org/D41206 llvm-svn: 321871
This commit is contained in:
parent
e565ebcdad
commit
cc254ba4a7
|
@ -0,0 +1,7 @@
|
|||
template<typename T>
|
||||
T abs(T x) {
|
||||
if (x < 0) {
|
||||
return -x;
|
||||
}
|
||||
return x;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#include "abs.h"
|
||||
#include "bytes.h"
|
||||
#include "pow.h"
|
||||
|
||||
bool loopBytes() {
|
||||
uint64_t totalInt = 0;
|
||||
double totalFloat = 0;
|
||||
for (uint8_t i = 1; i != 0; ++i) {
|
||||
double a = logarithm(i);
|
||||
a = abs(a);
|
||||
totalInt += abs(pow(i, static_cast<uint8_t>(a)));
|
||||
totalFloat += pow(static_cast<decltype(a)>(i), a);
|
||||
}
|
||||
return totalInt > totalFloat;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
inline double logarithm(uint8_t v) {
|
||||
return log(v);
|
||||
}
|
||||
|
||||
bool loopBytes();
|
|
@ -0,0 +1,15 @@
|
|||
#include "bytes.h"
|
||||
#include "words.h"
|
||||
|
||||
int main() {
|
||||
bool result = false;
|
||||
if (loopBytes())
|
||||
result |= true;
|
||||
if (loopWords())
|
||||
result |= true;
|
||||
|
||||
if (result)
|
||||
return 0;
|
||||
|
||||
return result;
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,11 @@
|
|||
template<typename T>
|
||||
T pow(T b, T p) {
|
||||
if (!p)
|
||||
return 1;
|
||||
|
||||
while (--p) {
|
||||
b *= b;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#include "abs.h"
|
||||
#include "bytes.h"
|
||||
#include "pow.h"
|
||||
|
||||
bool loopWords() {
|
||||
uint64_t totalInt = 0;
|
||||
double totalFloat = 0;
|
||||
for (uint16_t i = 1; i != 0; ++i) {
|
||||
double a = logarithm(i);
|
||||
a = abs(a);
|
||||
totalInt += abs(pow(i, static_cast<uint16_t>(a)));
|
||||
totalFloat += pow(static_cast<decltype(a)>(i), a);
|
||||
}
|
||||
return totalInt > totalFloat;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
inline double logarithm(uint16_t v) {
|
||||
return log(v);
|
||||
}
|
||||
|
||||
bool loopWords();
|
|
@ -0,0 +1,93 @@
|
|||
# Test "report" command with and without multiple threads.
|
||||
RUN: llvm-cov report -num-threads=1 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping > %t.1.report
|
||||
|
||||
RUN: llvm-cov report -num-threads=10 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping > %t.2.report
|
||||
|
||||
RUN: diff %t.1.report %t.2.report
|
||||
|
||||
# Test "export" command with and without multiple threads.
|
||||
RUN: llvm-cov export -num-threads=1 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping > %t.1.json
|
||||
|
||||
RUN: llvm-cov export -num-threads=10 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping > %t.2.json
|
||||
|
||||
RUN: diff %t.1.json %t.2.json
|
||||
|
||||
# Test "show" command with and without multiple threads, single text file.
|
||||
RUN: llvm-cov show -format=text -num-threads=1 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping > %t.1.text
|
||||
|
||||
RUN: llvm-cov show -format=text -num-threads=10 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping > %t.2.text
|
||||
|
||||
RUN: diff %t.1.text %t.2.text
|
||||
|
||||
# Test "show" command with and without multiple threads, single HTML file.
|
||||
RUN: llvm-cov show -format=html -num-threads=1 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping > %t.1.html
|
||||
|
||||
RUN: llvm-cov show -format=html -num-threads=10 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping > %t.2.html
|
||||
|
||||
RUN: diff %t.1.html %t.2.html
|
||||
|
||||
# Test "show" command with and without multiple threads, text directory.
|
||||
RUN: llvm-cov show -format=text -num-threads=1 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping -o %t.1.text_dir
|
||||
|
||||
RUN: llvm-cov show -format=text -num-threads=10 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping -o %t.2.text_dir
|
||||
|
||||
RUN: diff -r %t.1.text_dir %t.2.text_dir
|
||||
|
||||
# Test "show" command with and without multiple threads, HTML directory.
|
||||
RUN: llvm-cov show -format=html -num-threads=1 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping -o %t.1.html_dir
|
||||
|
||||
RUN: llvm-cov show -format=html -num-threads=10 \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: -instr-profile %S/Inputs/multithreaded_report/main.profdata \
|
||||
RUN: %S/Inputs/multithreaded_report/main.covmapping -o %t.2.html_dir
|
||||
|
||||
RUN: diff -r %t.1.html_dir %t.2.html_dir
|
||||
|
||||
|
||||
Instructions for regenerating the test:
|
||||
|
||||
# cd %S/Inputs/multithreaded_report
|
||||
|
||||
cp -r . /tmp/multithreaded_report
|
||||
|
||||
clang++ -std=c++11 -mllvm -enable-name-compression=false \
|
||||
-fprofile-instr-generate -fcoverage-mapping \
|
||||
/tmp/multithreaded_report/*.cc -o main
|
||||
|
||||
LLVM_PROFILE_FILE="main.profraw" ./main
|
||||
llvm-profdata merge main.profraw -o main.profdata
|
||||
llvm-cov convert-for-testing ./main -o ./main.covmapping
|
||||
rm main main.profraw
|
|
@ -33,8 +33,8 @@
|
|||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
#include "llvm/Support/Threading.h"
|
||||
#include "llvm/Support/ThreadPool.h"
|
||||
#include "llvm/Support/Threading.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
|
||||
#include <functional>
|
||||
|
@ -637,6 +637,12 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
|
|||
"summary-only", cl::Optional,
|
||||
cl::desc("Export only summary information for each source file"));
|
||||
|
||||
cl::opt<unsigned> NumThreads(
|
||||
"num-threads", cl::init(0),
|
||||
cl::desc("Number of merge threads to use (default: autodetect)"));
|
||||
cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
|
||||
cl::aliasopt(NumThreads));
|
||||
|
||||
auto commandLineParser = [&, this](int argc, const char **argv) -> int {
|
||||
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
|
||||
ViewOpts.Debug = DebugDump;
|
||||
|
@ -750,6 +756,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
|
|||
ViewOpts.ShowRegionSummary = RegionSummary;
|
||||
ViewOpts.ShowInstantiationSummary = InstantiationSummary;
|
||||
ViewOpts.ExportSummaryOnly = SummaryOnly;
|
||||
ViewOpts.NumThreads = NumThreads;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
@ -809,12 +816,6 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
|
|||
"project-title", cl::Optional,
|
||||
cl::desc("Set project title for the coverage report"));
|
||||
|
||||
cl::opt<unsigned> NumThreads(
|
||||
"num-threads", cl::init(0),
|
||||
cl::desc("Number of merge threads to use (default: autodetect)"));
|
||||
cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
|
||||
cl::aliasopt(NumThreads));
|
||||
|
||||
auto Err = commandLineParser(argc, argv);
|
||||
if (Err)
|
||||
return Err;
|
||||
|
@ -911,6 +912,8 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
|
|||
(SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
|
||||
(ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
|
||||
|
||||
auto NumThreads = ViewOpts.NumThreads;
|
||||
|
||||
// If NumThreads is not specified, auto-detect a good default.
|
||||
if (NumThreads == 0)
|
||||
NumThreads =
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/ThreadPool.h"
|
||||
#include "llvm/Support/Threading.h"
|
||||
#include <numeric>
|
||||
|
||||
using namespace llvm;
|
||||
|
@ -319,42 +321,60 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
|
|||
}
|
||||
}
|
||||
|
||||
void CoverageReport::prepareSingleFileReport(const StringRef Filename,
|
||||
const coverage::CoverageMapping *Coverage,
|
||||
const CoverageViewOptions &Options, const unsigned LCP,
|
||||
FileCoverageSummary *FileReport, const CoverageFilter *Filters) {
|
||||
for (const auto &Group : Coverage->getInstantiationGroups(Filename)) {
|
||||
std::vector<FunctionCoverageSummary> InstantiationSummaries;
|
||||
for (const coverage::FunctionRecord *F : Group.getInstantiations()) {
|
||||
if (!Filters->matches(*Coverage, *F))
|
||||
continue;
|
||||
auto InstantiationSummary = FunctionCoverageSummary::get(*Coverage, *F);
|
||||
FileReport->addInstantiation(InstantiationSummary);
|
||||
InstantiationSummaries.push_back(InstantiationSummary);
|
||||
}
|
||||
if (InstantiationSummaries.empty())
|
||||
continue;
|
||||
|
||||
auto GroupSummary =
|
||||
FunctionCoverageSummary::get(Group, InstantiationSummaries);
|
||||
|
||||
if (Options.Debug)
|
||||
outs() << "InstantiationGroup: " << GroupSummary.Name << " with "
|
||||
<< "size = " << Group.size() << "\n";
|
||||
|
||||
FileReport->addFunction(GroupSummary);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<FileCoverageSummary> CoverageReport::prepareFileReports(
|
||||
const coverage::CoverageMapping &Coverage, FileCoverageSummary &Totals,
|
||||
ArrayRef<std::string> Files, const CoverageViewOptions &Options,
|
||||
const CoverageFilter &Filters) {
|
||||
std::vector<FileCoverageSummary> FileReports;
|
||||
unsigned LCP = getRedundantPrefixLen(Files);
|
||||
auto NumThreads = Options.NumThreads;
|
||||
|
||||
// If NumThreads is not specified, auto-detect a good default.
|
||||
if (NumThreads == 0)
|
||||
NumThreads =
|
||||
std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(),
|
||||
unsigned(Files.size())));
|
||||
|
||||
ThreadPool Pool(NumThreads);
|
||||
|
||||
std::vector<FileCoverageSummary> FileReports;
|
||||
FileReports.reserve(Files.size());
|
||||
|
||||
for (StringRef Filename : Files) {
|
||||
FileCoverageSummary Summary(Filename.drop_front(LCP));
|
||||
|
||||
for (const auto &Group : Coverage.getInstantiationGroups(Filename)) {
|
||||
std::vector<FunctionCoverageSummary> InstantiationSummaries;
|
||||
for (const coverage::FunctionRecord *F : Group.getInstantiations()) {
|
||||
if (!Filters.matches(Coverage, *F))
|
||||
continue;
|
||||
auto InstantiationSummary = FunctionCoverageSummary::get(Coverage, *F);
|
||||
Summary.addInstantiation(InstantiationSummary);
|
||||
Totals.addInstantiation(InstantiationSummary);
|
||||
InstantiationSummaries.push_back(InstantiationSummary);
|
||||
}
|
||||
if (InstantiationSummaries.empty())
|
||||
continue;
|
||||
|
||||
auto GroupSummary =
|
||||
FunctionCoverageSummary::get(Group, InstantiationSummaries);
|
||||
|
||||
if (Options.Debug)
|
||||
outs() << "InstantiationGroup: " << GroupSummary.Name << " with "
|
||||
<< "size = " << Group.size() << "\n";
|
||||
|
||||
Summary.addFunction(GroupSummary);
|
||||
Totals.addFunction(GroupSummary);
|
||||
}
|
||||
|
||||
FileReports.push_back(Summary);
|
||||
FileReports.emplace_back(Filename.drop_front(LCP));
|
||||
Pool.async(&CoverageReport::prepareSingleFileReport, Filename,
|
||||
&Coverage, Options, LCP, &FileReports.back(), &Filters);
|
||||
}
|
||||
Pool.wait();
|
||||
|
||||
for (const auto &FileReport : FileReports)
|
||||
Totals += FileReport;
|
||||
|
||||
return FileReports;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,14 @@ public:
|
|||
const CoverageViewOptions &Options,
|
||||
const CoverageFilter &Filters = CoverageFiltersMatchAll());
|
||||
|
||||
static void
|
||||
prepareSingleFileReport(const StringRef Filename,
|
||||
const coverage::CoverageMapping *Coverage,
|
||||
const CoverageViewOptions &Options,
|
||||
const unsigned LCP,
|
||||
FileCoverageSummary *FileReport,
|
||||
const CoverageFilter *Filters);
|
||||
|
||||
/// Render file reports for every unique file in the coverage mapping.
|
||||
void renderFileReports(raw_ostream &OS) const;
|
||||
|
||||
|
|
|
@ -116,6 +116,12 @@ public:
|
|||
FunctionCoverageInfo(size_t Executed, size_t NumFunctions)
|
||||
: Executed(Executed), NumFunctions(NumFunctions) {}
|
||||
|
||||
FunctionCoverageInfo &operator+=(const FunctionCoverageInfo &RHS) {
|
||||
Executed += RHS.Executed;
|
||||
NumFunctions += RHS.NumFunctions;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void addFunction(bool Covered) {
|
||||
if (Covered)
|
||||
++Executed;
|
||||
|
@ -176,6 +182,14 @@ struct FileCoverageSummary {
|
|||
: Name(Name), RegionCoverage(), LineCoverage(), FunctionCoverage(),
|
||||
InstantiationCoverage() {}
|
||||
|
||||
FileCoverageSummary &operator+=(const FileCoverageSummary &RHS) {
|
||||
RegionCoverage += RHS.RegionCoverage;
|
||||
LineCoverage += RHS.LineCoverage;
|
||||
FunctionCoverage += RHS.FunctionCoverage;
|
||||
InstantiationCoverage += RHS.InstantiationCoverage;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void addFunction(const FunctionCoverageSummary &Function) {
|
||||
RegionCoverage += Function.RegionCoverage;
|
||||
LineCoverage += Function.LineCoverage;
|
||||
|
|
|
@ -39,6 +39,7 @@ struct CoverageViewOptions {
|
|||
uint32_t TabSize;
|
||||
std::string ProjectTitle;
|
||||
std::string CreatedTimeStr;
|
||||
unsigned NumThreads;
|
||||
|
||||
/// \brief Change the output's stream color if the colors are enabled.
|
||||
ColoredRawOstream colored_ostream(raw_ostream &OS,
|
||||
|
|
Loading…
Reference in New Issue