Skip to content

Commit

Permalink
[Profiling] Add a -sparse mode to llvm-profdata merge
Browse files Browse the repository at this point in the history
Add an option to llvm-profdata merge for writing out sparse indexed
profiles. These profiles omit InstrProfRecords for functions which are
never executed.

Differential Revision: http://reviews.llvm.org/D16727

llvm-svn: 259258
  • Loading branch information
vedantk committed Jan 29, 2016
1 parent b046154 commit 00dab22
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 44 deletions.
6 changes: 6 additions & 0 deletions llvm/docs/CommandGuide/llvm-profdata.rst
Expand Up @@ -90,6 +90,12 @@ OPTIONS

Emit the profile using GCC's gcov format (Not yet supported).

.. option:: -sparse[=true|false]

Do not emit function records with 0 execution count. Can only be used in
conjunction with -instr. Defaults to false, since it can inhibit compiler
optimization during PGO.

EXAMPLES
^^^^^^^^
Basic Usage
Expand Down
5 changes: 4 additions & 1 deletion llvm/include/llvm/ProfileData/InstrProfWriter.h
Expand Up @@ -32,13 +32,14 @@ class InstrProfWriter {
typedef SmallDenseMap<uint64_t, InstrProfRecord, 1> ProfilingData;

private:
bool Sparse;
StringMap<ProfilingData> FunctionData;
uint64_t MaxFunctionCount;
// Use raw pointer here for the incomplete type object.
InstrProfRecordWriterTrait *InfoObj;

public:
InstrProfWriter();
InstrProfWriter(bool Sparse = false);
~InstrProfWriter();

/// Add function counts for the given function. If there are already counts
Expand All @@ -57,8 +58,10 @@ class InstrProfWriter {

// Internal interface for testing purpose only.
void setValueProfDataEndianness(support::endianness Endianness);
void setOutputSparse(bool Sparse);

private:
bool shouldEncodeData(const ProfilingData &PD);
void writeImpl(ProfOStream &OS);
};

Expand Down
30 changes: 24 additions & 6 deletions llvm/lib/ProfileData/InstrProfWriter.cpp
Expand Up @@ -139,8 +139,8 @@ class InstrProfRecordWriterTrait {
};
}

InstrProfWriter::InstrProfWriter()
: FunctionData(), MaxFunctionCount(0),
InstrProfWriter::InstrProfWriter(bool Sparse)
: Sparse(Sparse), FunctionData(), MaxFunctionCount(0),
InfoObj(new InstrProfRecordWriterTrait()) {}

InstrProfWriter::~InstrProfWriter() { delete InfoObj; }
Expand All @@ -150,6 +150,9 @@ void InstrProfWriter::setValueProfDataEndianness(
support::endianness Endianness) {
InfoObj->ValueProfDataEndianness = Endianness;
}
void InstrProfWriter::setOutputSparse(bool Sparse) {
this->Sparse = Sparse;
}

std::error_code InstrProfWriter::addRecord(InstrProfRecord &&I,
uint64_t Weight) {
Expand Down Expand Up @@ -184,11 +187,24 @@ std::error_code InstrProfWriter::addRecord(InstrProfRecord &&I,
return Result;
}

bool InstrProfWriter::shouldEncodeData(const ProfilingData &PD) {
if (!Sparse)
return true;
for (const auto &Func : PD) {
const InstrProfRecord &IPR = Func.second;
if (std::any_of(IPR.Counts.begin(), IPR.Counts.end(),
[](uint64_t Count) { return Count > 0; }))
return true;
}
return false;
}

void InstrProfWriter::writeImpl(ProfOStream &OS) {
OnDiskChainedHashTableGenerator<InstrProfRecordWriterTrait> Generator;
// Populate the hash table generator.
for (const auto &I : FunctionData)
Generator.insert(I.getKey(), &I.getValue());
if (shouldEncodeData(I.getValue()))
Generator.insert(I.getKey(), &I.getValue());
// Write the header.
IndexedInstrProf::Header Header;
Header.Magic = IndexedInstrProf::Magic;
Expand Down Expand Up @@ -279,10 +295,12 @@ void InstrProfWriter::writeRecordInText(const InstrProfRecord &Func,
void InstrProfWriter::writeText(raw_fd_ostream &OS) {
InstrProfSymtab Symtab;
for (const auto &I : FunctionData)
Symtab.addFuncName(I.getKey());
if (shouldEncodeData(I.getValue()))
Symtab.addFuncName(I.getKey());
Symtab.finalizeSymtab();

for (const auto &I : FunctionData)
for (const auto &Func : I.getValue())
writeRecordInText(Func.second, Symtab, OS);
if (shouldEncodeData(I.getValue()))
for (const auto &Func : I.getValue())
writeRecordInText(Func.second, Symtab, OS);
}
18 changes: 11 additions & 7 deletions llvm/test/tools/llvm-profdata/general.proftext
@@ -1,6 +1,6 @@
# RUN: llvm-profdata merge -sparse=true %s -o %t.profdata


# RUN: llvm-profdata merge %s -o %t.profdata
# RUN: llvm-profdata merge -sparse=false %s -o %t.profdata.dense

# RUN: llvm-profdata show %t.profdata --function function_count_only --counts | FileCheck %s -check-prefix=FUNC_COUNT_ONLY
function_count_only
Expand All @@ -12,7 +12,8 @@ function_count_only
# FUNC_COUNT_ONLY-NEXT: Function count: 97531
# FUNC_COUNT_ONLY-NEXT: Block counts: []

# RUN: llvm-profdata show %t.profdata --function "name with spaces" --counts | FileCheck %s -check-prefix=SPACES
# RUN: llvm-profdata show %t.profdata.dense --function "name with spaces" --counts | FileCheck %s -check-prefix=SPACES
# RUN: llvm-profdata show %t.profdata --function "name with spaces" --counts | FileCheck %s --check-prefix=SPARSE_SPACES
name with spaces
1024
2
Expand All @@ -22,6 +23,7 @@ name with spaces
# SPACES-NEXT: Counters: 2
# SPACES-NEXT: Function count: 0
# SPACES-NEXT: Block counts: [0]
# SPARSE_SPACES-NOT: Function count: 0

# RUN: llvm-profdata show %t.profdata --function large_numbers --counts | FileCheck %s -check-prefix=LARGENUM
large_numbers
Expand All @@ -38,7 +40,7 @@ large_numbers
# LARGENUM-NEXT: Function count: 2305843009213693952
# LARGENUM-NEXT: Block counts: [1152921504606846976, 576460752303423488, 288230376151711744, 144115188075855872, 72057594037927936]

# RUN: llvm-profdata show %t.profdata --function hex_hash | FileCheck %s -check-prefix=HEX-HASH
# RUN: llvm-profdata show %t.profdata.dense --function hex_hash | FileCheck %s -check-prefix=HEX-HASH
hex_hash
0x1234
1
Expand All @@ -51,19 +53,21 @@ hex_hash
# NOSUCHFUNC: Functions shown: 0

# RUN: llvm-profdata show %t.profdata --function _ | FileCheck %s -check-prefix=SOMEFUNCS
# RUN: llvm-profdata show %t.profdata.dense --function _ | FileCheck %s -check-prefix=SOMEFUNCS_DENSE
# SOMEFUNCS: Counters:
# SOMEFUNCS: function_count_only:
# SOMEFUNCS: large_numbers:
# SOMEFUNCS: Functions shown: 3
# SOMEFUNCS: Functions shown: 2
# SOMEFUNCS_DENSE: Functions shown: 3

# RUN: llvm-profdata show %t.profdata | FileCheck %s -check-prefix=SUMMARY
# RUN: llvm-profdata show %t.profdata.dense | FileCheck %s -check-prefix=SUMMARY
# SUMMARY-NOT: Counters:
# SUMMARY-NOT: Functions shown:
# SUMMARY: Total functions: 4
# SUMMARY: Maximum function count: 2305843009213693952
# SUMMARY: Maximum internal block count: 1152921504606846976

# RUN: llvm-profdata show --detailed-summary %t.profdata | FileCheck %s -check-prefix=DETAILED-SUMMARY
# RUN: llvm-profdata show --detailed-summary %t.profdata.dense | FileCheck %s -check-prefix=DETAILED-SUMMARY
# DETAILED-SUMMARY: Detailed summary:
# DETAILED-SUMMARY: Total number of blocks: 10
# DETAILED-SUMMARY: Total count: 4539628424389557499
Expand Down
10 changes: 7 additions & 3 deletions llvm/tools/llvm-profdata/llvm-profdata.cpp
Expand Up @@ -107,7 +107,7 @@ typedef SmallVector<WeightedFile, 5> WeightedFileVector;

static void mergeInstrProfile(const WeightedFileVector &Inputs,
StringRef OutputFilename,
ProfileFormat OutputFormat) {
ProfileFormat OutputFormat, bool OutputSparse) {
if (OutputFilename.compare("-") == 0)
exitWithError("Cannot write indexed profdata format to stdout.");

Expand All @@ -119,7 +119,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
if (EC)
exitWithErrorCode(EC, OutputFilename);

InstrProfWriter Writer;
InstrProfWriter Writer(OutputSparse);
SmallSet<std::error_code, 4> WriterErrorCodes;
for (const auto &Input : Inputs) {
auto ReaderOrErr = InstrProfReader::create(Input.Filename);
Expand Down Expand Up @@ -228,6 +228,9 @@ static int merge_main(int argc, const char *argv[]) {
"GCC encoding (only meaningful for -sample)"),
clEnumValEnd));

cl::opt<bool> OutputSparse("sparse", cl::init(false),
cl::desc("Generate a sparse profile (only meaningful for -instr)"));

cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");

if (InputFilenames.empty() && WeightedInputFilenames.empty())
Expand All @@ -241,7 +244,8 @@ static int merge_main(int argc, const char *argv[]) {
WeightedInputs.push_back(parseWeightedFile(WeightedFilename));

if (ProfileKind == instr)
mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat);
mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat,
OutputSparse);
else
mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat);

Expand Down
37 changes: 26 additions & 11 deletions llvm/unittests/ProfileData/CoverageMappingTest.cpp
Expand Up @@ -92,6 +92,7 @@ struct CoverageMappingTest : ::testing::Test {

void SetUp() override {
NextFile = 0;
ProfileWriter.setOutputSparse(false);
}

unsigned getFile(StringRef Name) {
Expand Down Expand Up @@ -154,7 +155,16 @@ struct CoverageMappingTest : ::testing::Test {
}
};

TEST_F(CoverageMappingTest, basic_write_read) {
struct MaybeSparseCoverageMappingTest
: public CoverageMappingTest,
public ::testing::WithParamInterface<bool> {
void SetUp() {
CoverageMappingTest::SetUp();
ProfileWriter.setOutputSparse(GetParam());
}
};

TEST_P(MaybeSparseCoverageMappingTest, basic_write_read) {
addCMR(Counter::getCounter(0), "foo", 1, 1, 1, 1);
addCMR(Counter::getCounter(1), "foo", 2, 1, 2, 2);
addCMR(Counter::getZero(), "foo", 3, 1, 3, 4);
Expand All @@ -174,7 +184,7 @@ TEST_F(CoverageMappingTest, basic_write_read) {
}
}

TEST_F(CoverageMappingTest, expansion_gets_first_counter) {
TEST_P(MaybeSparseCoverageMappingTest, expansion_gets_first_counter) {
addCMR(Counter::getCounter(1), "foo", 10, 1, 10, 2);
// This starts earlier in "foo", so the expansion should get its counter.
addCMR(Counter::getCounter(2), "foo", 1, 1, 20, 1);
Expand All @@ -187,7 +197,7 @@ TEST_F(CoverageMappingTest, expansion_gets_first_counter) {
ASSERT_EQ(3U, OutputCMRs[2].LineStart);
}

TEST_F(CoverageMappingTest, basic_coverage_iteration) {
TEST_P(MaybeSparseCoverageMappingTest, basic_coverage_iteration) {
InstrProfRecord Record("func", 0x1234, {30, 20, 10, 0});
ProfileWriter.addRecord(std::move(Record));
readProfCounts();
Expand All @@ -210,7 +220,7 @@ TEST_F(CoverageMappingTest, basic_coverage_iteration) {
ASSERT_EQ(CoverageSegment(11, 11, false), Segments[6]);
}

TEST_F(CoverageMappingTest, uncovered_function) {
TEST_P(MaybeSparseCoverageMappingTest, uncovered_function) {
readProfCounts();

addCMR(Counter::getZero(), "file1", 1, 2, 3, 4);
Expand All @@ -223,7 +233,7 @@ TEST_F(CoverageMappingTest, uncovered_function) {
ASSERT_EQ(CoverageSegment(3, 4, false), Segments[1]);
}

TEST_F(CoverageMappingTest, uncovered_function_with_mapping) {
TEST_P(MaybeSparseCoverageMappingTest, uncovered_function_with_mapping) {
readProfCounts();

addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
Expand All @@ -238,7 +248,7 @@ TEST_F(CoverageMappingTest, uncovered_function_with_mapping) {
ASSERT_EQ(CoverageSegment(9, 9, false), Segments[2]);
}

TEST_F(CoverageMappingTest, combine_regions) {
TEST_P(MaybeSparseCoverageMappingTest, combine_regions) {
InstrProfRecord Record("func", 0x1234, {10, 20, 30});
ProfileWriter.addRecord(std::move(Record));
readProfCounts();
Expand All @@ -257,9 +267,11 @@ TEST_F(CoverageMappingTest, combine_regions) {
ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);
}

TEST_F(CoverageMappingTest, dont_combine_expansions) {
InstrProfRecord Record("func", 0x1234, {10, 20});
ProfileWriter.addRecord(std::move(Record));
TEST_P(MaybeSparseCoverageMappingTest, dont_combine_expansions) {
InstrProfRecord Record1("func", 0x1234, {10, 20});
InstrProfRecord Record2("func", 0x1234, {0, 0});
ProfileWriter.addRecord(std::move(Record1));
ProfileWriter.addRecord(std::move(Record2));
readProfCounts();

addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
Expand All @@ -277,8 +289,8 @@ TEST_F(CoverageMappingTest, dont_combine_expansions) {
ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);
}

TEST_F(CoverageMappingTest, strip_filename_prefix) {
InstrProfRecord Record("file1:func", 0x1234, {10});
TEST_P(MaybeSparseCoverageMappingTest, strip_filename_prefix) {
InstrProfRecord Record("file1:func", 0x1234, {0});
ProfileWriter.addRecord(std::move(Record));
readProfCounts();

Expand All @@ -292,4 +304,7 @@ TEST_F(CoverageMappingTest, strip_filename_prefix) {
ASSERT_EQ("func", Names[0]);
}

INSTANTIATE_TEST_CASE_P(MaybeSparse, MaybeSparseCoverageMappingTest,
::testing::Bool());

} // end anonymous namespace

0 comments on commit 00dab22

Please sign in to comment.