Skip to content

Commit

Permalink
[PDB] Optimize public symbol processing
Browse files Browse the repository at this point in the history
Reduces time to link PGO instrumented net_unittets.exe by 11% (9.766s ->
8.672s, best of three). Reduces peak memory by 65.7MB (2142.71MB ->
2076.95MB).

Use a more compact struct, BulkPublic, for faster sorting. Sort in
parallel. Construct the hash buckets in parallel. Try to use one vector
to hold all the publics instead of copying them from one to another.
Allocate all the memory needed to serialize publics up front, and then
serialize them in place in parallel.

Reviewed By: aganea, hans

Differential Revision: https://reviews.llvm.org/D79467
  • Loading branch information
rnk committed May 8, 2020
1 parent 8cb86ea commit 3b3e28a
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 139 deletions.
41 changes: 23 additions & 18 deletions lld/COFF/PDB.cpp
Expand Up @@ -56,7 +56,6 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include <memory>
Expand All @@ -75,7 +74,7 @@ static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer);
static Timer typeMergingTimer("Type Merging", addObjectsTimer);
static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer);
static Timer globalsLayoutTimer("Globals Stream Layout", totalPdbLinkTimer);
static Timer publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer);
static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer);
static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer);

Expand Down Expand Up @@ -106,6 +105,9 @@ class PDBLinker {
/// Link CodeView from each object file in the symbol table into the PDB.
void addObjectsToPDB();

/// Add every live, defined public symbol to the PDB.
void addPublicsToPDB();

/// Link info for each import file in the symbol table into the PDB.
void addImportFilesToPDB(ArrayRef<OutputSection *> outputSections);

Expand Down Expand Up @@ -1295,20 +1297,24 @@ static void createModuleDBI(pdb::PDBFileBuilder &builder) {
}
}

static PublicSym32 createPublic(Defined *def) {
PublicSym32 pub(SymbolKind::S_PUB32);
pub.Name = def->getName();
static pdb::BulkPublic createPublic(Defined *def) {
pdb::BulkPublic pub;
pub.Name = def->getName().data();
pub.NameLen = def->getName().size();

PublicSymFlags flags = PublicSymFlags::None;
if (auto *d = dyn_cast<DefinedCOFF>(def)) {
if (d->getCOFFSymbol().isFunctionDefinition())
pub.Flags = PublicSymFlags::Function;
flags = PublicSymFlags::Function;
} else if (isa<DefinedImportThunk>(def)) {
pub.Flags = PublicSymFlags::Function;
flags = PublicSymFlags::Function;
}
pub.Flags = static_cast<uint16_t>(flags);

OutputSection *os = def->getChunk()->getOutputSection();
assert(os && "all publics should be in final image");
pub.Offset = def->getRVA() - os->getRVA();
pub.Segment = os->sectionIndex;
pub.U.Segment = os->sectionIndex;
return pub;
}

Expand All @@ -1330,26 +1336,24 @@ void PDBLinker::addObjectsToPDB() {
addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable());
t2.stop();
}

ScopedTimer t3(globalsLayoutTimer);
// Compute the public and global symbols.
void PDBLinker::addPublicsToPDB() {
ScopedTimer t3(publicsLayoutTimer);
// Compute the public symbols.
auto &gsiBuilder = builder.getGsiBuilder();
std::vector<PublicSym32> publics;
std::vector<pdb::BulkPublic> publics;
symtab->forEachSymbol([&publics](Symbol *s) {
// Only emit defined, live symbols that have a chunk.
// Only emit external, defined, live symbols that have a chunk. Static,
// non-external symbols do not appear in the symbol table.
auto *def = dyn_cast<Defined>(s);
if (def && def->isLive() && def->getChunk())
publics.push_back(createPublic(def));
});

if (!publics.empty()) {
publicSymbols = publics.size();
// Sort the public symbols and add them to the stream.
parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) {
return l.Name < r.Name;
});
for (const PublicSym32 &pub : publics)
gsiBuilder.addPublicSymbol(pub);
gsiBuilder.addPublicSymbols(std::move(publics));
}
}

Expand Down Expand Up @@ -1708,6 +1712,7 @@ void lld::coff::createPDB(SymbolTable *symtab,
pdb.addSections(outputSections, sectionTable);
pdb.addNatvisFiles();
pdb.addNamedStreams();
pdb.addPublicsToPDB();

ScopedTimer t2(diskCommitTimer);
codeview::GUID guid;
Expand Down
37 changes: 36 additions & 1 deletion llvm/include/llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h
Expand Up @@ -37,6 +37,7 @@ struct MSFLayout;
} // namespace msf
namespace pdb {
struct GSIHashStreamBuilder;
struct BulkPublic;

class GSIStreamBuilder {

Expand All @@ -55,7 +56,8 @@ class GSIStreamBuilder {
uint32_t getGlobalsStreamIndex() const { return GlobalsStreamIndex; }
uint32_t getRecordStreamIndex() const { return RecordStreamIndex; }

void addPublicSymbol(const codeview::PublicSym32 &Pub);
// Add public symbols in bulk.
void addPublicSymbols(std::vector<BulkPublic> &&Publics);

void addGlobalSymbol(const codeview::ProcRefSym &Sym);
void addGlobalSymbol(const codeview::DataSym &Sym);
Expand All @@ -75,7 +77,40 @@ class GSIStreamBuilder {
msf::MSFBuilder &Msf;
std::unique_ptr<GSIHashStreamBuilder> PSH;
std::unique_ptr<GSIHashStreamBuilder> GSH;
std::vector<support::ulittle32_t> PubAddrMap;
};

/// This struct is equivalent to codeview::PublicSym32, but it has been
/// optimized for size to speed up bulk serialization and sorting operations
/// during PDB writing.
struct BulkPublic {
const char *Name = nullptr;
uint32_t NameLen = 0;

// Offset of the symbol record in the publics stream.
uint32_t SymOffset = 0;

// Section offset of the symbol in the image.
uint32_t Offset = 0;

union {
// Section index of the section containing the symbol.
uint16_t Segment;

// GSI hash table bucket index.
uint16_t BucketIdx;
} U{0};

// PublicSymFlags or hash bucket index
uint16_t Flags = 0;

StringRef getName() const { return StringRef(Name, NameLen); }
};

static_assert(sizeof(BulkPublic) <= 24, "unexpected size increase");
static_assert(std::is_trivially_copyable<BulkPublic>::value,
"should be trivial");

} // namespace pdb
} // namespace llvm

Expand Down

0 comments on commit 3b3e28a

Please sign in to comment.