From: Douglas Gregor Date: Mon, 28 Jan 2013 16:46:33 +0000 (+0000) Subject: Eliminate memory allocation from most invocations of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d3cf5fba332fc77f7e72ef58077822606718671d;p=clang Eliminate memory allocation from most invocations of ModuleManager::visit() by keeping a free list of the two data structures used to store state (a preallocated stack and a visitation number vector). Improves -fsyntax-only performance for my modules test case by 2.8%. Modules has pulled ahead by almost 10% with the global module index. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@173692 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Serialization/ModuleManager.h b/include/clang/Serialization/ModuleManager.h index 05d702664e..60e3b40a7a 100644 --- a/include/clang/Serialization/ModuleManager.h +++ b/include/clang/Serialization/ModuleManager.h @@ -43,7 +43,7 @@ class ModuleManager { /// \brief The visitation order. SmallVector VisitOrder; - + /// \brief The list of module files that both we and the global module index /// know about. /// @@ -63,6 +63,40 @@ class ModuleManager { /// \brief Update the set of modules files we know about known to the global index. void updateModulesInCommonWithGlobalIndex(); + /// \brief State used by the "visit" operation to avoid malloc traffic in + /// calls to visit(). + struct VisitState { + explicit VisitState(unsigned N) + : VisitNumber(N, 0), NextVisitNumber(1), NextState(0) + { + Stack.reserve(N); + } + + ~VisitState() { + delete NextState; + } + + /// \brief The stack used when marking the imports of a particular module + /// as not-to-be-visited. + SmallVector Stack; + + /// \brief The visit number of each module file, which indicates when + /// this module file was last visited. + SmallVector VisitNumber; + + /// \brief The next visit number to use to mark visited module files. + unsigned NextVisitNumber; + + /// \brief The next visit state. + VisitState *NextState; + }; + + /// \brief The first visit() state in the chain. + VisitState *FirstVisitState; + + VisitState *allocateVisitState(); + void returnVisitState(VisitState *State); + public: typedef SmallVector::iterator ModuleIterator; typedef SmallVector::const_iterator ModuleConstIterator; diff --git a/lib/Serialization/ModuleManager.cpp b/lib/Serialization/ModuleManager.cpp index 97c86a7bb8..bc9917b582 100644 --- a/lib/Serialization/ModuleManager.cpp +++ b/lib/Serialization/ModuleManager.cpp @@ -162,17 +162,37 @@ void ModuleManager::updateModulesInCommonWithGlobalIndex() { } } +ModuleManager::VisitState *ModuleManager::allocateVisitState() { + // Fast path: if we have a cached state, use it. + if (FirstVisitState) { + VisitState *Result = FirstVisitState; + FirstVisitState = FirstVisitState->NextState; + Result->NextState = 0; + return Result; + } + + // Allocate and return a new state. + return new VisitState(size()); +} + +void ModuleManager::returnVisitState(VisitState *State) { + assert(State->NextState == 0 && "Visited state is in list?"); + State->NextState = FirstVisitState; + FirstVisitState = State; +} + void ModuleManager::setGlobalIndex(GlobalModuleIndex *Index) { GlobalIndex = Index; updateModulesInCommonWithGlobalIndex(); } ModuleManager::ModuleManager(FileManager &FileMgr) - : FileMgr(FileMgr), GlobalIndex() { } + : FileMgr(FileMgr), GlobalIndex(), FirstVisitState(0) { } ModuleManager::~ModuleManager() { for (unsigned i = 0, e = Chain.size(); i != e; ++i) delete Chain[e - i - 1]; + delete FirstVisitState; } void @@ -230,10 +250,13 @@ ModuleManager::visit(bool (*Visitor)(ModuleFile &M, void *UserData), // global module index, since modules could have been added to the module // manager since we loaded the global module index. updateModulesInCommonWithGlobalIndex(); + + delete FirstVisitState; + FirstVisitState = 0; } - SmallVector Stack; - SmallVector Visited(size(), false); + VisitState *State = allocateVisitState(); + unsigned VisitNumber = State->NextVisitNumber++; // If the caller has provided us with a hit-set that came from the global // module index, mark every module file in common with the global module @@ -243,18 +266,19 @@ ModuleManager::visit(bool (*Visitor)(ModuleFile &M, void *UserData), { ModuleFile *M = ModulesInCommonWithGlobalIndex[I]; if (!ModuleFilesHit->count(M->File)) - Visited[M->Index] = true; + State->VisitNumber[M->Index] = VisitNumber; } } for (unsigned I = 0, N = VisitOrder.size(); I != N; ++I) { ModuleFile *CurrentModule = VisitOrder[I]; // Should we skip this module file? - if (Visited[CurrentModule->Index]) + if (State->VisitNumber[CurrentModule->Index] == VisitNumber) continue; // Visit the module. - Visited[CurrentModule->Index] = true; + assert(State->VisitNumber[CurrentModule->Index] == VisitNumber - 1); + State->VisitNumber[CurrentModule->Index] = VisitNumber; if (!Visitor(*CurrentModule, UserData)) continue; @@ -262,7 +286,6 @@ ModuleManager::visit(bool (*Visitor)(ModuleFile &M, void *UserData), // module that the current module depends on. To indicate this // behavior, we mark all of the reachable modules as having been visited. ModuleFile *NextModule = CurrentModule; - Stack.reserve(size()); do { // For any module that this module depends on, push it on the // stack (if it hasn't already been marked as visited). @@ -270,20 +293,22 @@ ModuleManager::visit(bool (*Visitor)(ModuleFile &M, void *UserData), M = NextModule->Imports.begin(), MEnd = NextModule->Imports.end(); M != MEnd; ++M) { - if (!Visited[(*M)->Index]) { - Stack.push_back(*M); - Visited[(*M)->Index] = true; + if (State->VisitNumber[(*M)->Index] != VisitNumber) { + State->Stack.push_back(*M); + State->VisitNumber[(*M)->Index] = VisitNumber; } } - if (Stack.empty()) + if (State->Stack.empty()) break; // Pop the next module off the stack. - NextModule = Stack.back(); - Stack.pop_back(); + NextModule = State->Stack.back(); + State->Stack.pop_back(); } while (true); } + + returnVisitState(State); } /// \brief Perform a depth-first visit of the current module.