From 8fd6cd1184e15c9ee4da9458818b58c589755c83 Mon Sep 17 00:00:00 2001 From: Max Neunhoeffer Date: Fri, 22 Aug 2014 17:00:53 +0200 Subject: [PATCH] Implement clear unneeded registers. --- arangod/Aql/AqlItemBlock.cpp | 28 +++++++ arangod/Aql/AqlItemBlock.h | 7 ++ arangod/Aql/ExecutionBlock.cpp | 129 +++++++++++++++++++++++++++++---- arangod/Aql/ExecutionBlock.h | 40 ++++++++++ arangod/Aql/ExecutionNode.cpp | 46 +++++++++++- arangod/Aql/ExecutionNode.h | 56 +++++++------- arangod/Aql/ExecutionPlan.cpp | 17 ++++- arangod/Aql/ExecutionPlan.h | 14 +++- arangod/Aql/Optimizer.cpp | 4 +- 9 files changed, 290 insertions(+), 51 deletions(-) diff --git a/arangod/Aql/AqlItemBlock.cpp b/arangod/Aql/AqlItemBlock.cpp index c8758b9fac..6ba57c7cdf 100644 --- a/arangod/Aql/AqlItemBlock.cpp +++ b/arangod/Aql/AqlItemBlock.cpp @@ -118,6 +118,34 @@ void AqlItemBlock::shrink (size_t nrItems) { _nrItems = nrItems; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief clears out some columns (registers), this deletes the values if +/// necessary, using the reference count. +//////////////////////////////////////////////////////////////////////////////// + +void AqlItemBlock::clearRegisters (std::unordered_set& toClear) { + for (auto reg : toClear) { + for (size_t i = 0; i < _nrItems; i++) { + AqlValue& a(_data[_nrRegs * i + reg]); + if (! a.isEmpty()) { + auto it = _valueCount.find(a); + if (it != _valueCount.end()) { + if (--it->second == 0) { + try { + _valueCount.erase(it); + } + catch (...) { + it->second++; + throw; + } + } + } + a.erase(); + } + } + } +} + //////////////////////////////////////////////////////////////////////////////// /// @brief slice/clone, this does a deep copy of all entries //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/AqlItemBlock.h b/arangod/Aql/AqlItemBlock.h index e9e86846d8..62b0071795 100644 --- a/arangod/Aql/AqlItemBlock.h +++ b/arangod/Aql/AqlItemBlock.h @@ -239,6 +239,13 @@ namespace triagens { void shrink (size_t nrItems); +//////////////////////////////////////////////////////////////////////////////// +/// @brief clears out some columns (registers), this deletes the values if +/// necessary, using the reference count. +//////////////////////////////////////////////////////////////////////////////// + + void clearRegisters (std::unordered_set& toClear); + //////////////////////////////////////////////////////////////////////////////// /// @brief slice/clone, this does a deep copy of all entries //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 34282e2c9f..1383a4c073 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -194,6 +194,45 @@ void ExecutionBlock::walk (WalkerWorker* worker) { /// @brief static analysis //////////////////////////////////////////////////////////////////////////////// +struct StaticAnalysisDebugger : public WalkerWorker { + StaticAnalysisDebugger () : indent(0) {}; + ~StaticAnalysisDebugger () {}; + + int indent; + + bool enterSubquery (ExecutionBlock* super, ExecutionBlock* sub) { + indent++; + return true; + } + + void leaveSubquery (ExecutionBlock* super, ExecutionBlock* sub) { + indent--; + } + + void after (ExecutionBlock* eb) { + ExecutionNode const* ep = eb->getPlanNode(); + for (int i = 0; i < indent; i++) { + std::cout << " "; + } + std::cout << ep->getTypeString() << " "; + std::cout << "regsUsedHere: "; + for (auto v : ep->getVariablesUsedHere()) { + std::cout << eb->_varOverview->varInfo.find(v->id)->second.registerId + << " "; + } + std::cout << "regsSetHere: "; + for (auto v : ep->getVariablesSetHere()) { + std::cout << eb->_varOverview->varInfo.find(v->id)->second.registerId + << " "; + } + std::cout << "regsToClear: "; + for (auto r : eb->_regsToClear) { + std::cout << r << " "; + } + std::cout << std::endl; + } +}; + void ExecutionBlock::staticAnalysis (ExecutionBlock* super) { // The super is only for the case of subqueries. shared_ptr v; @@ -211,6 +250,14 @@ void ExecutionBlock::staticAnalysis (ExecutionBlock* super) { sq->getSubquery()->staticAnalysis(s); } v->reset(); + + // Just for debugging: + /* + std::cout << std::endl; + StaticAnalysisDebugger debugger; + walk(&debugger); + std::cout << std::endl; + */ } int ExecutionBlock::initialize () { @@ -250,19 +297,20 @@ void ExecutionBlock::inheritRegisters (AqlItemBlock const* src, RegisterId const n = src->getNrRegs(); for (RegisterId i = 0; i < n; i++) { - if (! src->getValue(row, i).isEmpty()) { - AqlValue a = src->getValue(row, i).clone(); - try { - dst->setValue(0, i, a); - } - catch (...) { - a.destroy(); - throw; + if (_regsToClear.find(i) == _regsToClear.end()) { + if (! src->getValue(row, i).isEmpty()) { + AqlValue a = src->getValue(row, i).clone(); + try { + dst->setValue(0, i, a); + } + catch (...) { + a.destroy(); + throw; + } } + // copy collection + dst->setDocumentCollection(i, src->getDocumentCollection(i)); } - - // copy collection - dst->setDocumentCollection(i, src->getDocumentCollection(i)); } } @@ -281,7 +329,14 @@ bool ExecutionBlock::getBlock (size_t atLeast, size_t atMost) { return true; } -AqlItemBlock* ExecutionBlock::getSome (size_t atLeast, size_t atMost) { +AqlItemBlock* ExecutionBlock::getSome(size_t atLeast, size_t atMost) { + std::unique_ptr result(getSomeWithoutRegisterClearout(atLeast, atMost)); + clearRegisters(result.get()); + return result.release(); +} + +AqlItemBlock* ExecutionBlock::getSomeWithoutRegisterClearout ( + size_t atLeast, size_t atMost) { TRI_ASSERT(0 < atLeast && atLeast <= atMost); size_t skipped = 0; AqlItemBlock* result = nullptr; @@ -292,6 +347,13 @@ AqlItemBlock* ExecutionBlock::getSome (size_t atLeast, size_t atMost) { return result; } +void ExecutionBlock::clearRegisters (AqlItemBlock* result) { + // Clear out registers not needed later on: + if (result != nullptr) { + result->clearRegisters(_regsToClear); + } +} + size_t ExecutionBlock::skipSome (size_t atLeast, size_t atMost) { TRI_ASSERT(0 < atLeast && atLeast <= atMost); size_t skipped = 0; @@ -656,6 +718,8 @@ AqlItemBlock* EnumerateCollectionBlock::getSome (size_t atLeast, } } } + // Clear out registers no longer needed later: + clearRegisters(res.get()); return res.release(); } @@ -831,6 +895,8 @@ AqlItemBlock* IndexRangeBlock::getSome (size_t atLeast, } } } + // Clear out registers no longer needed later: + clearRegisters(res.get()); return res.release(); } @@ -1048,6 +1114,9 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t atLeast, size_t atMost) { } while (res.get() == nullptr); + // Clear out registers no longer needed later: + clearRegisters(res.get()); + return res.release(); } @@ -1249,13 +1318,16 @@ void CalculationBlock::doEvaluation (AqlItemBlock* result) { AqlItemBlock* CalculationBlock::getSome (size_t atLeast, size_t atMost) { - unique_ptr res(ExecutionBlock::getSome(atLeast, atMost)); + unique_ptr res(ExecutionBlock::getSomeWithoutRegisterClearout( + atLeast, atMost)); if (res.get() == nullptr) { return nullptr; } doEvaluation(res.get()); + // Clear out registers no longer needed later: + clearRegisters(res.get()); return res.release(); } @@ -1282,7 +1354,8 @@ int SubqueryBlock::initialize () { AqlItemBlock* SubqueryBlock::getSome (size_t atLeast, size_t atMost) { - unique_ptr res(ExecutionBlock::getSome(atLeast, atMost)); + unique_ptr res(ExecutionBlock::getSomeWithoutRegisterClearout( + atLeast, atMost)); if (res.get() == nullptr) { return nullptr; @@ -1315,6 +1388,8 @@ AqlItemBlock* SubqueryBlock::getSome (size_t atLeast, throw; } } + // Clear out registers no longer needed later: + clearRegisters(res.get()); return res.release(); } @@ -2029,7 +2104,7 @@ int LimitBlock::getOrSkipSome (size_t atLeast, AqlItemBlock* ReturnBlock::getSome (size_t atLeast, size_t atMost) { - auto res = ExecutionBlock::getSome(atLeast, atMost); + auto res = ExecutionBlock::getSomeWithoutRegisterClearout(atLeast, atMost); if (res == nullptr) { return res; @@ -2107,7 +2182,7 @@ AqlItemBlock* ModificationBlock::getSome (size_t atLeast, // loop over input until it is exhausted try { while (true) { - auto res = ExecutionBlock::getSome(atLeast, atMost); + auto res = ExecutionBlock::getSomeWithoutRegisterClearout(atLeast, atMost); if (res == nullptr) { break; @@ -2707,6 +2782,28 @@ void ExecutionBlock::VarOverview::after (ExecutionBlock *eb) { } eb->_depth = depth; eb->_varOverview = *me; + + // Now find out which registers ought to be erased after this node: + auto ep = eb->getPlanNode(); + if (ep->getType() != ExecutionNode::RETURN) { + // ReturnBlocks are special, since they return a single column anyway + std::unordered_set const& varsUsedLater = ep->getVarsUsedLater(); + std::vector const& varsUsedHere = ep->getVariablesUsedHere(); + + // We need to delete those variables that have been used here but are not + // used any more later: + std::unordered_set regsToClear; + for (auto v : varsUsedHere) { + auto it = varsUsedLater.find(v); + if (it == varsUsedLater.end()) { + auto it2 = varInfo.find(v->id); + TRI_ASSERT(it2 != varInfo.end()); + RegisterId r = it2->second.registerId; + regsToClear.insert(r); + } + } + eb->setRegsToClear(regsToClear); + } } // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index c870b90744..6ce4ced621 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -294,6 +294,27 @@ namespace triagens { virtual AqlItemBlock* getSome (size_t atLeast, size_t atMost); +//////////////////////////////////////////////////////////////////////////////// +/// @brief getSomeWithoutRegisterClearout, same as above, however, this +/// is the actual worker which does not clear out registers at the end +/// the idea is that somebody who wants to call the generic functionality +/// in a derived class but wants to modify the results before the register +/// cleanup can use this method, internal use only +//////////////////////////////////////////////////////////////////////////////// + + protected: + + AqlItemBlock* getSomeWithoutRegisterClearout (size_t atLeast, size_t atMost); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clearRegisters, clears out registers holding values that are no +/// longer needed by later nodes +//////////////////////////////////////////////////////////////////////////////// + + void clearRegisters (AqlItemBlock* result); + + public: + //////////////////////////////////////////////////////////////////////////////// /// @brief getSome, skips some more items, semantic is as follows: not /// more than atMost items may be skipped. The method tries to @@ -320,6 +341,14 @@ namespace triagens { return _exeNode; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief set regs to be deleted +//////////////////////////////////////////////////////////////////////////////// + + void setRegsToClear (std::unordered_set& toClear) { + _regsToClear = toClear; + } + protected: //////////////////////////////////////////////////////////////////////////////// @@ -372,6 +401,7 @@ namespace triagens { /// @brief info about variables, filled in by staticAnalysis //////////////////////////////////////////////////////////////////////////////// +public: std::shared_ptr _varOverview; //////////////////////////////////////////////////////////////////////////////// @@ -392,6 +422,16 @@ namespace triagens { bool _done; +//////////////////////////////////////////////////////////////////////////////// +/// @brief the following contains the registers which should be cleared +/// just before this node hands on results. This is computed during +/// the static analysis for each node using the variable usage in the plan. +//////////////////////////////////////////////////////////////////////////////// + +public: + + std::unordered_set _regsToClear; + // ----------------------------------------------------------------------------- // --SECTION-- public variables // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index 74f99cf52b..7364199ecd 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -635,7 +635,7 @@ struct SubqueryVarUsageFinder : public WalkerWorker { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// -std::vector SubqueryNode::getVariablesUsedHere () { +std::vector SubqueryNode::getVariablesUsedHere () const { SubqueryVarUsageFinder finder; _subquery->walk(&finder); @@ -755,6 +755,50 @@ void AggregateNode::toJsonHelper (triagens::basics::Json& nodes, nodes(json); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief getVariablesUsedHere +//////////////////////////////////////////////////////////////////////////////// + +struct UserVarFinder : public WalkerWorker { + UserVarFinder () {}; + ~UserVarFinder () {}; + std::vector userVars; + + bool enterSubquery (ExecutionNode* super, ExecutionNode* sub) { + return false; + } + void before (ExecutionNode* en) { + auto vars = en->getVariablesSetHere(); + for (auto v : vars) { + if (v->isUserDefined()) { + userVars.push_back(v); + } + } + } +}; + +std::vector AggregateNode::getVariablesUsedHere () const { + std::unordered_set v; + for (auto p : _aggregateVariables) { + v.insert(p.second); + } + if (_outVariable != nullptr) { + // Here we have to find all user defined variables in this query + // amonst our dependencies: + UserVarFinder finder; + auto myselfasnonconst = const_cast(this); + myselfasnonconst->walk(&finder); + for (auto x : finder.userVars) { + v.insert(x); + } + } + std::vector vv; + for (auto x : v) { + vv.push_back(x); + } + return vv; +} + // ----------------------------------------------------------------------------- // --SECTION-- methods of ReturnNode // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/ExecutionNode.h b/arangod/Aql/ExecutionNode.h index add69bc6ff..bf7b06c88e 100644 --- a/arangod/Aql/ExecutionNode.h +++ b/arangod/Aql/ExecutionNode.h @@ -364,7 +364,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { return std::vector(); } @@ -372,7 +372,7 @@ namespace triagens { /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { return std::vector(); } @@ -388,7 +388,7 @@ namespace triagens { /// @brief getVarsUsedLater //////////////////////////////////////////////////////////////////////////////// - std::unordered_set& getVarsUsedLater () { + std::unordered_set const& getVarsUsedLater () const { TRI_ASSERT(_varUsageValid); return _varsUsedLater; } @@ -405,7 +405,7 @@ namespace triagens { /// @brief getVarsValid //////////////////////////////////////////////////////////////////////////////// - std::unordered_set& getVarsValid () { + std::unordered_set const& getVarsValid () const { TRI_ASSERT(_varUsageValid); return _varsValid; } @@ -648,7 +648,7 @@ namespace triagens { /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; v.push_back(_outVariable); return v; @@ -770,7 +770,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { std::vector v; v.push_back(_inVariable); return v; @@ -780,7 +780,7 @@ namespace triagens { /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; v.push_back(_outVariable); return v; @@ -885,7 +885,7 @@ namespace triagens { /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; v.push_back(_outVariable); return v; @@ -1110,7 +1110,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { std::unordered_set vars = _expression->variables(); std::vector v; for (auto vv : vars) { @@ -1123,7 +1123,7 @@ namespace triagens { /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; v.push_back(_outVariable); return v; @@ -1248,13 +1248,13 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere (); + virtual std::vector getVariablesUsedHere () const; //////////////////////////////////////////////////////////////////////////////// /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; v.push_back(_outVariable); return v; @@ -1358,7 +1358,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { std::vector v; v.push_back(_inVariable); return v; @@ -1442,7 +1442,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { std::vector v; for (auto p : _elements) { v.push_back(p.first); @@ -1541,19 +1541,13 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { - std::vector v; - for (auto p : _aggregateVariables) { - v.push_back(p.second); - } - return v; - } + virtual std::vector getVariablesUsedHere () const; //////////////////////////////////////////////////////////////////////////////// /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; for (auto p : _aggregateVariables) { v.push_back(p.first); @@ -1658,7 +1652,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { std::vector v; v.push_back(_inVariable); return v; @@ -1815,7 +1809,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { std::vector v; v.push_back(_inVariable); return v; @@ -1825,7 +1819,7 @@ namespace triagens { /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; if (_outVariable != nullptr) { v.push_back(_outVariable); @@ -1927,7 +1921,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { std::vector v; v.push_back(_inVariable); return v; @@ -1937,7 +1931,7 @@ namespace triagens { /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; if (_outVariable != nullptr) { v.push_back(_outVariable); @@ -2042,7 +2036,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { std::vector v; v.push_back(_inDocVariable); @@ -2056,7 +2050,7 @@ namespace triagens { /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; if (_outVariable != nullptr) { v.push_back(_outVariable); @@ -2167,7 +2161,7 @@ namespace triagens { /// @brief getVariablesUsedHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesUsedHere () { + virtual std::vector getVariablesUsedHere () const { std::vector v; v.push_back(_inDocVariable); @@ -2181,7 +2175,7 @@ namespace triagens { /// @brief getVariablesSetHere //////////////////////////////////////////////////////////////////////////////// - virtual std::vector getVariablesSetHere () { + virtual std::vector getVariablesSetHere () const { std::vector v; if (_outVariable != nullptr) { v.push_back(_outVariable); diff --git a/arangod/Aql/ExecutionPlan.cpp b/arangod/Aql/ExecutionPlan.cpp index 46da2cb43f..45307a8a95 100644 --- a/arangod/Aql/ExecutionPlan.cpp +++ b/arangod/Aql/ExecutionPlan.cpp @@ -53,6 +53,7 @@ using JsonHelper = triagens::basics::JsonHelper; ExecutionPlan::ExecutionPlan () : _ids(), _root(nullptr), + _varUsageComputed(false), _nextId(0) { } @@ -997,6 +998,15 @@ void ExecutionPlan::findVarUsage () { VarUsageFinder finder; root()->walk(&finder); _varSetBy = finder._varSetBy; + _varUsageComputed = true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief determine if the above are already set +//////////////////////////////////////////////////////////////////////////////// + +bool ExecutionPlan::varUsageComputed () { + return _varUsageComputed; } //////////////////////////////////////////////////////////////////////////////// @@ -1033,6 +1043,7 @@ void ExecutionPlan::unlinkNode (ExecutionNode* node) { node->removeDependency(x); } } + _varUsageComputed = false; } //////////////////////////////////////////////////////////////////////////////// @@ -1060,6 +1071,7 @@ void ExecutionPlan::replaceNode (ExecutionNode* oldNode, "Could not replace dependencies of an old node."); } } + _varUsageComputed = false; } //////////////////////////////////////////////////////////////////////////////// @@ -1082,6 +1094,7 @@ void ExecutionPlan::insertDependency (ExecutionNode* oldNode, newNode->removeDependencies(); newNode->addDependency(oldDeps[0]); + _varUsageComputed = false; } //////////////////////////////////////////////////////////////////////////////// @@ -1109,7 +1122,9 @@ ExecutionPlan* ExecutionPlan::clone (){ plan->_nextId = _nextId; CloneNodeAdder adder(plan); plan->_root->walk(&adder); - plan->findVarUsage(); + // plan->findVarUsage(); + // Let's not do it here, because supposedly the plan is modified as + // the very next thing anyway! return plan; } catch (...) { diff --git a/arangod/Aql/ExecutionPlan.h b/arangod/Aql/ExecutionPlan.h index 4add3e7362..9580446cb5 100644 --- a/arangod/Aql/ExecutionPlan.h +++ b/arangod/Aql/ExecutionPlan.h @@ -155,11 +155,17 @@ namespace triagens { void checkLinkage (); //////////////////////////////////////////////////////////////////////////////// -/// @brief determine and set _varsUsedLater in all nodes +/// @brief determine and set _varsUsedLater and _valid and _varSetBy //////////////////////////////////////////////////////////////////////////////// void findVarUsage (); +//////////////////////////////////////////////////////////////////////////////// +/// @brief determine if the above are already set +//////////////////////////////////////////////////////////////////////////////// + + bool varUsageComputed (); + //////////////////////////////////////////////////////////////////////////////// /// @brief unlinkNodes, note that this does not delete the removed /// nodes and that one cannot remove the root node of the plan. @@ -358,6 +364,12 @@ namespace triagens { std::unordered_map _varSetBy; +//////////////////////////////////////////////////////////////////////////////// +/// @brief flag to indicate whether the variable usage is computed +//////////////////////////////////////////////////////////////////////////////// + + bool _varUsageComputed; + //////////////////////////////////////////////////////////////////////////////// /// @brief auto-increment sequence for node ids //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Optimizer.cpp b/arangod/Aql/Optimizer.cpp index 9c3f342c09..a4ca60e8ee 100644 --- a/arangod/Aql/Optimizer.cpp +++ b/arangod/Aql/Optimizer.cpp @@ -89,7 +89,9 @@ int Optimizer::createPlans (ExecutionPlan* plan) { // Find variable usage for all old plans now: for (auto p : oldPlans.list) { - p->findVarUsage(); + if (! p->varUsageComputed()) { + p->findVarUsage(); + } } // For all rules: