diff options
author | Wei Mi <wmi@google.com> | 2018-09-14 20:52:59 +0000 |
---|---|---|
committer | Wei Mi <wmi@google.com> | 2018-09-14 20:52:59 +0000 |
commit | 6a14325dffb7a0607e5b3c75d93a837cadad54b4 (patch) | |
tree | b66e8dff99504155918123b84a96f9577a905c10 | |
parent | 11511ab5cf306c7c688917bdd69e47dac7292756 (diff) | |
download | bcm5719-llvm-6a14325dffb7a0607e5b3c75d93a837cadad54b4.tar.gz bcm5719-llvm-6a14325dffb7a0607e5b3c75d93a837cadad54b4.zip |
[SampleFDO] Add FunctionOffsetTable in compact binary format profile.
The patch saves a function offset table which maps function name index to the
offset of its function profile to the start of the binary profile. By using
the function offset table, for those function profiles which will not be used
when compiling a module, the profile reader does't have to read them. For
profile size around 10~20M, it saves ~10% compile time.
Differential Revision: https://reviews.llvm.org/D51863
llvm-svn: 342283
-rw-r--r-- | llvm/include/llvm/ProfileData/SampleProf.h | 3 | ||||
-rw-r--r-- | llvm/include/llvm/ProfileData/SampleProfReader.h | 24 | ||||
-rw-r--r-- | llvm/include/llvm/ProfileData/SampleProfWriter.h | 51 | ||||
-rw-r--r-- | llvm/lib/ProfileData/SampleProf.cpp | 2 | ||||
-rw-r--r-- | llvm/lib/ProfileData/SampleProfReader.cpp | 111 | ||||
-rw-r--r-- | llvm/lib/ProfileData/SampleProfWriter.cpp | 57 | ||||
-rw-r--r-- | llvm/lib/Transforms/IPO/SampleProfile.cpp | 1 | ||||
-rw-r--r-- | llvm/test/Transforms/SampleProfile/Inputs/function_metadata.compact.afdo | bin | 280 -> 295 bytes | |||
-rw-r--r-- | llvm/test/Transforms/SampleProfile/Inputs/indirect-call.compact.afdo | bin | 497 -> 533 bytes | |||
-rw-r--r-- | llvm/test/Transforms/SampleProfile/Inputs/inline.compactbinary.afdo | bin | 172 -> 184 bytes | |||
-rw-r--r-- | llvm/unittests/ProfileData/SampleProfTest.cpp | 20 |
11 files changed, 245 insertions, 24 deletions
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h index 65fa8a8ef0e..e632a1c955b 100644 --- a/llvm/include/llvm/ProfileData/SampleProf.h +++ b/llvm/include/llvm/ProfileData/SampleProf.h @@ -49,7 +49,8 @@ enum class sampleprof_error { unsupported_writing_format, truncated_name_table, not_implemented, - counter_overflow + counter_overflow, + ostream_seek_unsupported }; inline std::error_code make_error_code(sampleprof_error E) { diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h index 0617b05e8d4..c100e800464 100644 --- a/llvm/include/llvm/ProfileData/SampleProfReader.h +++ b/llvm/include/llvm/ProfileData/SampleProfReader.h @@ -279,6 +279,8 @@ public: /// Print the profile for \p FName on stream \p OS. void dumpFunctionProfile(StringRef FName, raw_ostream &OS = dbgs()); + virtual void collectFuncsToUse(const Module &M) {} + /// Print all the profiles on stream \p OS. void dump(raw_ostream &OS = dbgs()); @@ -364,7 +366,7 @@ public: : SampleProfileReader(std::move(B), C, Format) {} /// Read and validate the file header. - std::error_code readHeader() override; + virtual std::error_code readHeader() override; /// Read sample profiles from the associated file. std::error_code read() override; @@ -378,6 +380,10 @@ protected: /// \returns the read value. template <typename T> ErrorOr<T> readNumber(); + /// Read a numeric value of type T from the profile. The value is saved + /// without encoded. + template <typename T> ErrorOr<T> readUnencodedNumber(); + /// Read a string from the profile. /// /// If an error occurs during decoding, a diagnostic message is emitted and @@ -392,6 +398,9 @@ protected: /// Return true if we've reached the end of file. bool at_eof() const { return Data >= End; } + /// Read the next function profile instance. + std::error_code readFuncProfile(); + /// Read the contents of the given profile instance. std::error_code readProfile(FunctionSamples &FProfile); @@ -436,10 +445,17 @@ class SampleProfileReaderCompactBinary : public SampleProfileReaderBinary { private: /// Function name table. std::vector<std::string> NameTable; + /// The table mapping from function name to the offset of its FunctionSample + /// towards file start. + DenseMap<StringRef, uint64_t> FuncOffsetTable; + /// The set containing the functions to use when compiling a module. + DenseSet<StringRef> FuncsToUse; virtual std::error_code verifySPMagic(uint64_t Magic) override; virtual std::error_code readNameTable() override; /// Read a string indirectly via the name table. virtual ErrorOr<StringRef> readStringFromTable() override; + virtual std::error_code readHeader() override; + std::error_code readFuncOffsetTable(); public: SampleProfileReaderCompactBinary(std::unique_ptr<MemoryBuffer> B, @@ -448,6 +464,12 @@ public: /// \brief Return true if \p Buffer is in the format supported by this class. static bool hasFormat(const MemoryBuffer &Buffer); + + /// Read samples only for functions to use. + std::error_code read() override; + + /// Collect functions to be used when compiling Module \p M. + void collectFuncsToUse(const Module &M) override; }; using InlineCallStack = SmallVector<FunctionSamples *, 10>; diff --git a/llvm/include/llvm/ProfileData/SampleProfWriter.h b/llvm/include/llvm/ProfileData/SampleProfWriter.h index 74dc839ff04..d5ac6e53e4f 100644 --- a/llvm/include/llvm/ProfileData/SampleProfWriter.h +++ b/llvm/include/llvm/ProfileData/SampleProfWriter.h @@ -42,7 +42,7 @@ public: /// Write all the sample profiles in the given map of samples. /// /// \returns status code of the file update operation. - std::error_code write(const StringMap<FunctionSamples> &ProfileMap); + virtual std::error_code write(const StringMap<FunctionSamples> &ProfileMap); raw_ostream &getOutputStream() { return *OutputStream; } @@ -103,14 +103,15 @@ private: /// Sample-based profile writer (binary format). class SampleProfileWriterBinary : public SampleProfileWriter { public: - std::error_code write(const FunctionSamples &S) override; + virtual std::error_code write(const FunctionSamples &S) override; SampleProfileWriterBinary(std::unique_ptr<raw_ostream> &OS) : SampleProfileWriter(OS) {} protected: virtual std::error_code writeNameTable() = 0; virtual std::error_code writeMagicIdent() = 0; - std::error_code writeHeader(const StringMap<FunctionSamples> &ProfileMap) override; + virtual std::error_code + writeHeader(const StringMap<FunctionSamples> &ProfileMap) override; std::error_code writeSummary(); std::error_code writeNameIdx(StringRef FName); std::error_code writeBody(const FunctionSamples &S); @@ -135,12 +136,56 @@ protected: virtual std::error_code writeMagicIdent() override; }; +// CompactBinary is a compact format of binary profile which both reduces +// the profile size and the load time needed when compiling. It has two +// major difference with Binary format. +// 1. It represents all the strings in name table using md5 hash. +// 2. It saves a function offset table which maps function name index to +// the offset of its function profile to the start of the binary profile, +// so by using the function offset table, for those function profiles which +// will not be needed when compiling a module, the profile reader does't +// have to read them and it saves compile time if the profile size is huge. +// The layout of the compact format is shown as follows: +// +// Part1: Profile header, the same as binary format, containing magic +// number, version, summary, name table... +// Part2: Function Offset Table Offset, which saves the position of +// Part4. +// Part3: Function profile collection +// function1 profile start +// .... +// function2 profile start +// .... +// function3 profile start +// .... +// ...... +// Part4: Function Offset Table +// function1 name index --> function1 profile start +// function2 name index --> function2 profile start +// function3 name index --> function3 profile start +// +// We need Part2 because profile reader can use it to find out and read +// function offset table without reading Part3 first. class SampleProfileWriterCompactBinary : public SampleProfileWriterBinary { using SampleProfileWriterBinary::SampleProfileWriterBinary; +public: + virtual std::error_code write(const FunctionSamples &S) override; + virtual std::error_code + write(const StringMap<FunctionSamples> &ProfileMap) override; + protected: + /// The table mapping from function name to the offset of its FunctionSample + /// towards profile start. + MapVector<StringRef, uint64_t> FuncOffsetTable; + /// The offset of the slot to be filled with the offset of FuncOffsetTable + /// towards profile start. + uint64_t TableOffset; virtual std::error_code writeNameTable() override; virtual std::error_code writeMagicIdent() override; + virtual std::error_code + writeHeader(const StringMap<FunctionSamples> &ProfileMap) override; + std::error_code writeFuncOffsetTable(); }; } // end namespace sampleprof diff --git a/llvm/lib/ProfileData/SampleProf.cpp b/llvm/lib/ProfileData/SampleProf.cpp index b0818d12475..1a124415f17 100644 --- a/llvm/lib/ProfileData/SampleProf.cpp +++ b/llvm/lib/ProfileData/SampleProf.cpp @@ -67,6 +67,8 @@ class SampleProfErrorCategoryType : public std::error_category { return "Unimplemented feature"; case sampleprof_error::counter_overflow: return "Counter overflow"; + case sampleprof_error::ostream_seek_unsupported: + return "Ostream does not support seek"; } llvm_unreachable("A value of sampleprof_error has no message."); } diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp index 5503104c1ef..2b4551b9849 100644 --- a/llvm/lib/ProfileData/SampleProfReader.cpp +++ b/llvm/lib/ProfileData/SampleProfReader.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/LineIterator.h" +#include "llvm/Support/MD5.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -320,6 +321,21 @@ ErrorOr<StringRef> SampleProfileReaderBinary::readString() { } template <typename T> +ErrorOr<T> SampleProfileReaderBinary::readUnencodedNumber() { + std::error_code EC; + + if (Data + sizeof(T) > End) { + EC = sampleprof_error::truncated; + reportError(0, EC.message()); + return EC; + } + + using namespace support; + T Val = endian::readNext<T, little, unaligned>(Data); + return Val; +} + +template <typename T> inline ErrorOr<uint32_t> SampleProfileReaderBinary::readStringIndex(T &Table) { std::error_code EC; auto Idx = readNumber<uint32_t>(); @@ -423,29 +439,51 @@ SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) { return sampleprof_error::success; } -std::error_code SampleProfileReaderBinary::read() { - while (!at_eof()) { - auto NumHeadSamples = readNumber<uint64_t>(); - if (std::error_code EC = NumHeadSamples.getError()) - return EC; +std::error_code SampleProfileReaderBinary::readFuncProfile() { + auto NumHeadSamples = readNumber<uint64_t>(); + if (std::error_code EC = NumHeadSamples.getError()) + return EC; - auto FName(readStringFromTable()); - if (std::error_code EC = FName.getError()) - return EC; + auto FName(readStringFromTable()); + if (std::error_code EC = FName.getError()) + return EC; - Profiles[*FName] = FunctionSamples(); - FunctionSamples &FProfile = Profiles[*FName]; - FProfile.setName(*FName); + Profiles[*FName] = FunctionSamples(); + FunctionSamples &FProfile = Profiles[*FName]; + FProfile.setName(*FName); - FProfile.addHeadSamples(*NumHeadSamples); + FProfile.addHeadSamples(*NumHeadSamples); + + if (std::error_code EC = readProfile(FProfile)) + return EC; + return sampleprof_error::success; +} - if (std::error_code EC = readProfile(FProfile)) +std::error_code SampleProfileReaderBinary::read() { + while (!at_eof()) { + if (std::error_code EC = readFuncProfile()) return EC; } return sampleprof_error::success; } +std::error_code SampleProfileReaderCompactBinary::read() { + for (auto Name : FuncsToUse) { + auto GUID = std::to_string(MD5Hash(Name)); + auto iter = FuncOffsetTable.find(StringRef(GUID)); + if (iter == FuncOffsetTable.end()) + continue; + const uint8_t *SavedData = Data; + Data = reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()) + + iter->second; + if (std::error_code EC = readFuncProfile()) + return EC; + Data = SavedData; + } + return sampleprof_error::success; +} + std::error_code SampleProfileReaderRawBinary::verifySPMagic(uint64_t Magic) { if (Magic == SPMagic()) return sampleprof_error::success; @@ -514,6 +552,53 @@ std::error_code SampleProfileReaderBinary::readHeader() { return sampleprof_error::success; } +std::error_code SampleProfileReaderCompactBinary::readHeader() { + SampleProfileReaderBinary::readHeader(); + if (std::error_code EC = readFuncOffsetTable()) + return EC; + return sampleprof_error::success; +} + +std::error_code SampleProfileReaderCompactBinary::readFuncOffsetTable() { + auto TableOffset = readUnencodedNumber<uint64_t>(); + if (std::error_code EC = TableOffset.getError()) + return EC; + + const uint8_t *SavedData = Data; + const uint8_t *TableStart = + reinterpret_cast<const uint8_t *>(Buffer->getBufferStart()) + + *TableOffset; + Data = TableStart; + + auto Size = readNumber<uint64_t>(); + if (std::error_code EC = Size.getError()) + return EC; + + FuncOffsetTable.reserve(*Size); + for (uint32_t I = 0; I < *Size; ++I) { + auto FName(readStringFromTable()); + if (std::error_code EC = FName.getError()) + return EC; + + auto Offset = readNumber<uint64_t>(); + if (std::error_code EC = Offset.getError()) + return EC; + + FuncOffsetTable[*FName] = *Offset; + } + End = TableStart; + Data = SavedData; + return sampleprof_error::success; +} + +void SampleProfileReaderCompactBinary::collectFuncsToUse(const Module &M) { + FuncsToUse.clear(); + for (auto &F : M) { + StringRef Fname = F.getName().split('.').first; + FuncsToUse.insert(Fname); + } +} + std::error_code SampleProfileReaderBinary::readSummaryEntry( std::vector<ProfileSummaryEntry> &Entries) { auto Cutoff = readNumber<uint64_t>(); diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp index b4de30118b8..b1c669ec31c 100644 --- a/llvm/lib/ProfileData/SampleProfWriter.cpp +++ b/llvm/lib/ProfileData/SampleProfWriter.cpp @@ -22,6 +22,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ProfileData/ProfileCommon.h" #include "llvm/ProfileData/SampleProf.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/EndianStream.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/LEB128.h" @@ -64,6 +66,15 @@ SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) { return sampleprof_error::success; } +std::error_code SampleProfileWriterCompactBinary::write( + const StringMap<FunctionSamples> &ProfileMap) { + if (std::error_code EC = SampleProfileWriter::write(ProfileMap)) + return EC; + if (std::error_code EC = writeFuncOffsetTable()) + return EC; + return sampleprof_error::success; +} + /// Write samples to a text file. /// /// Note: it may be tempting to implement this in terms of @@ -168,6 +179,30 @@ std::error_code SampleProfileWriterRawBinary::writeNameTable() { return sampleprof_error::success; } +std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() { + auto &OS = *OutputStream; + + // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable. + auto &OFS = static_cast<raw_fd_ostream &>(OS); + uint64_t FuncOffsetTableStart = OS.tell(); + if (OFS.seek(TableOffset) == (uint64_t)-1) + return sampleprof_error::ostream_seek_unsupported; + support::endian::Writer Writer(*OutputStream, support::little); + Writer.write(FuncOffsetTableStart); + if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1) + return sampleprof_error::ostream_seek_unsupported; + + // Write out the table size. + encodeULEB128(FuncOffsetTable.size(), OS); + + // Write out FuncOffsetTable. + for (auto entry : FuncOffsetTable) { + writeNameIdx(entry.first); + encodeULEB128(entry.second, OS); + } + return sampleprof_error::success; +} + std::error_code SampleProfileWriterCompactBinary::writeNameTable() { auto &OS = *OutputStream; std::set<StringRef> V; @@ -215,6 +250,19 @@ std::error_code SampleProfileWriterBinary::writeHeader( return sampleprof_error::success; } +std::error_code SampleProfileWriterCompactBinary::writeHeader( + const StringMap<FunctionSamples> &ProfileMap) { + support::endian::Writer Writer(*OutputStream, support::little); + if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap)) + return EC; + + // Reserve a slot for the offset of function offset table. The slot will + // be populated with the offset of FuncOffsetTable later. + TableOffset = OutputStream->tell(); + Writer.write(static_cast<uint64_t>(-2)); + return sampleprof_error::success; +} + std::error_code SampleProfileWriterBinary::writeSummary() { auto &OS = *OutputStream; encodeULEB128(Summary->getTotalCount(), OS); @@ -283,6 +331,15 @@ std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) { return writeBody(S); } +std::error_code +SampleProfileWriterCompactBinary::write(const FunctionSamples &S) { + uint64_t Offset = OutputStream->tell(); + StringRef Name = S.getName(); + FuncOffsetTable[Name] = Offset; + encodeULEB128(S.getHeadSamples(), *OutputStream); + return writeBody(S); +} + /// Create a sample profile file writer based on the specified format. /// /// \param Filename The file to create. diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp index b9b055d36f7..41ed0614af0 100644 --- a/llvm/lib/Transforms/IPO/SampleProfile.cpp +++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp @@ -1515,6 +1515,7 @@ bool SampleProfileLoader::doInitialization(Module &M) { return false; } Reader = std::move(ReaderOrErr.get()); + Reader->collectFuncsToUse(M); ProfileIsValid = (Reader->read() == sampleprof_error::success); return true; } diff --git a/llvm/test/Transforms/SampleProfile/Inputs/function_metadata.compact.afdo b/llvm/test/Transforms/SampleProfile/Inputs/function_metadata.compact.afdo Binary files differindex 16d7d008301..20bd8967077 100644 --- a/llvm/test/Transforms/SampleProfile/Inputs/function_metadata.compact.afdo +++ b/llvm/test/Transforms/SampleProfile/Inputs/function_metadata.compact.afdo diff --git a/llvm/test/Transforms/SampleProfile/Inputs/indirect-call.compact.afdo b/llvm/test/Transforms/SampleProfile/Inputs/indirect-call.compact.afdo Binary files differindex b43ac6fcd60..579f03c8515 100644 --- a/llvm/test/Transforms/SampleProfile/Inputs/indirect-call.compact.afdo +++ b/llvm/test/Transforms/SampleProfile/Inputs/indirect-call.compact.afdo diff --git a/llvm/test/Transforms/SampleProfile/Inputs/inline.compactbinary.afdo b/llvm/test/Transforms/SampleProfile/Inputs/inline.compactbinary.afdo Binary files differindex 805809952f0..6271b3609e9 100644 --- a/llvm/test/Transforms/SampleProfile/Inputs/inline.compactbinary.afdo +++ b/llvm/test/Transforms/SampleProfile/Inputs/inline.compactbinary.afdo diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp index 3ebfd0e500f..6c669445a2b 100644 --- a/llvm/unittests/ProfileData/SampleProfTest.cpp +++ b/llvm/unittests/ProfileData/SampleProfTest.cpp @@ -36,14 +36,17 @@ static ::testing::AssertionResult NoError(std::error_code EC) { namespace { struct SampleProfTest : ::testing::Test { - std::string Data; LLVMContext Context; + std::string Profile; std::unique_ptr<raw_ostream> OS; std::unique_ptr<SampleProfileWriter> Writer; std::unique_ptr<SampleProfileReader> Reader; + std::error_code EC; SampleProfTest() - : Data(), OS(new raw_string_ostream(Data)), Writer(), Reader() {} + : Profile("profile"), + OS(new raw_fd_ostream(Profile, EC, sys::fs::F_None)), Writer(), + Reader() {} void createWriter(SampleProfileFormat Format) { auto WriterOrErr = SampleProfileWriter::create(OS, Format); @@ -51,10 +54,11 @@ struct SampleProfTest : ::testing::Test { Writer = std::move(WriterOrErr.get()); } - void readProfile(std::unique_ptr<MemoryBuffer> &Profile) { + void readProfile(const Module &M) { auto ReaderOrErr = SampleProfileReader::create(Profile, Context); ASSERT_TRUE(NoError(ReaderOrErr.getError())); Reader = std::move(ReaderOrErr.get()); + Reader->collectFuncsToUse(M); } void testRoundTrip(SampleProfileFormat Format) { @@ -83,6 +87,12 @@ struct SampleProfTest : ::testing::Test { BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000); BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437); + Module M("my_module", Context); + FunctionType *fn_type = + FunctionType::get(Type::getVoidTy(Context), {}, false); + M.getOrInsertFunction(FooName, fn_type); + M.getOrInsertFunction(BarName, fn_type); + StringMap<FunctionSamples> Profiles; Profiles[FooName] = std::move(FooSamples); Profiles[BarName] = std::move(BarSamples); @@ -93,8 +103,7 @@ struct SampleProfTest : ::testing::Test { Writer->getOutputStream().flush(); - auto Profile = MemoryBuffer::getMemBufferCopy(Data); - readProfile(Profile); + readProfile(M); EC = Reader->read(); ASSERT_TRUE(NoError(EC)); @@ -164,7 +173,6 @@ struct SampleProfTest : ::testing::Test { delete PS; // Test that summary can be attached to and read back from module. - Module M("my_module", Context); M.setProfileSummary(MD); MD = M.getProfileSummary(); ASSERT_TRUE(MD); |