From 845e1cdca3349c72e3083186502285d5b776abbe Mon Sep 17 00:00:00 2001 From: Dennis Francis Date: Wed, 29 May 2019 10:28:22 +0530 Subject: Thread a group of formula-groups together if possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just before about to thread a FG, look to left and right for "mutually" independent FG's with some restrictions and thread this group of FG's together treating it as a single but longer computation load. For now the restrictions are :- All formula-groups in a FG "group" must have :- 1. Same length 2. Same relative position. 3. Same weight. This is very helpful in cases similar to the below : There are lots of (say 32) consecutive formula-groups all with same "small" length (say 8) and same weight. By conventional formula-group-threading the speed-up is limited to 8x even if we have a 256 core processor, but with this threading-multiple-formula-groups patch (in this case) we can get a speed-up of 256x provided we have a >= 256 core machine. So effectively with this patch the speed-up is now only limited to the number of cells in a range consisting of mutually indepdendent formula-groups rather than number of cells in each formula-group. Change-Id: Ib25b5abbb583fa207e8befff9a908d14313f3d51 Reviewed-on: https://gerrit.libreoffice.org/79485 Tested-by: Jenkins Reviewed-by: Luboš Luňák --- sc/inc/column.hxx | 3 +- sc/inc/document.hxx | 4 +- sc/inc/formulacell.hxx | 2 +- sc/inc/interpretercontext.hxx | 3 +- sc/inc/recursionhelper.hxx | 22 ++++++ sc/inc/table.hxx | 5 +- sc/source/core/data/column2.cxx | 5 +- sc/source/core/data/documen8.cxx | 14 ++-- sc/source/core/data/formulacell.cxx | 117 ++++++++++++++++++++++++++++++-- sc/source/core/data/table1.cxx | 22 ++++-- sc/source/core/tool/recursionhelper.cxx | 44 ++++++++++++ 11 files changed, 213 insertions(+), 28 deletions(-) diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx index 78272c7f4f3b..3b7ffbe645e1 100644 --- a/sc/inc/column.hxx +++ b/sc/inc/column.hxx @@ -593,7 +593,8 @@ public: #endif void SetFormulaResults( SCROW nRow, const double* pResults, size_t nLen ); - void CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal ); + void CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, size_t nLen, size_t nOffset, + unsigned nThisThread, unsigned nThreadsTotal ); void HandleStuffAfterParallelCalculation( SCROW nRow, size_t nLen ); void SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat ); diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 9ac4bca15288..2b2ae8d99c0c 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -2148,8 +2148,8 @@ public: */ void SC_DLLPUBLIC SetFormulaResults( const ScAddress& rTopPos, const double* pResults, size_t nLen ); - const ScDocumentThreadSpecific& CalculateInColumnInThread( ScInterpreterContext& rContext, const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal); - void HandleStuffAfterParallelCalculation( const ScAddress& rTopPos, size_t nLen ); + const ScDocumentThreadSpecific& CalculateInColumnInThread( ScInterpreterContext& rContext, const ScRange& rCalcRange, unsigned nThisThread, unsigned nThreadsTotal); + void HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen, SCTAB nTab ); /** * Transfer a series of contiguous cell values from specified position to diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx index fe421508ea18..65a3a4af7733 100644 --- a/sc/inc/formulacell.hxx +++ b/sc/inc/formulacell.hxx @@ -146,7 +146,7 @@ private: ScFormulaCell( const ScFormulaCell& ) = delete; bool CheckComputeDependencies(sc::FormulaLogger::GroupScope& rScope, bool fromFirstRow, - SCROW nStartOffset, SCROW nEndOffset); + SCROW nStartOffset, SCROW nEndOffset, bool bCalcDependencyOnly = false); bool InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope& aScope, bool& bDependencyComputed, bool& bDependencyCheckFailed, diff --git a/sc/inc/interpretercontext.hxx b/sc/inc/interpretercontext.hxx index 9fb2bc3993c5..c7598fef8cdc 100644 --- a/sc/inc/interpretercontext.hxx +++ b/sc/inc/interpretercontext.hxx @@ -28,7 +28,8 @@ struct ScLookupCacheMap; // SetNumberFormat() is not thread-safe, so calls to it need to be delayed to the main thread. struct DelayedSetNumberFormat { - SCROW mRow; // Used only with formula groups, so column and tab do not need to be stored. + SCROW mCol; + SCROW mRow; sal_uInt32 mnNumberFormat; }; diff --git a/sc/inc/recursionhelper.hxx b/sc/inc/recursionhelper.hxx index 2a72be7b9d76..5962f11eb61a 100644 --- a/sc/inc/recursionhelper.hxx +++ b/sc/inc/recursionhelper.hxx @@ -25,6 +25,7 @@ #include #include #include +#include class ScFormulaCell; @@ -59,7 +60,9 @@ class ScRecursionHelper bool bDoingRecursion; bool bInIterationReturn; bool bConverging; + bool bGroupsIndependent; std::vector< ScFormulaCell* > aTemporaryGroupCells; + std::unordered_set< ScFormulaCellGroup* >* pFGSet; void Init(); void ResetIteration(); @@ -111,6 +114,12 @@ public: void AddTemporaryGroupCell(ScFormulaCell* cell); void CleanTemporaryGroupCells(); + + void SetFormulaGroupSet(std::unordered_set* pSet) { pFGSet = pSet; } + bool HasFormulaGroupSet() { return pFGSet != nullptr; } + bool CheckFGIndependence(ScFormulaCellGroup* pFG); + void SetGroupsIndependent(bool bSet) { bGroupsIndependent = bSet; } + bool AreGroupsIndependent() { return bGroupsIndependent; } }; /** A class to wrap ScRecursionHelper::PushFormulaGroup(), @@ -136,6 +145,19 @@ public: ~ScFormulaGroupDependencyComputeGuard(); }; +class ScCheckIndependentFGGuard +{ + ScRecursionHelper& mrRecHelper; + bool mbUsedFGSet; +public: + ScCheckIndependentFGGuard() = delete; + ScCheckIndependentFGGuard(ScRecursionHelper& rRecursionHelper, + std::unordered_set* pSet); + ~ScCheckIndependentFGGuard(); + + bool AreGroupsIndependent(); +}; + #endif // INCLUDED_SC_INC_RECURSIONHELPER_HXX /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index b3ea9016725e..78bfa854e33d 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -1012,8 +1012,9 @@ public: void SetFormulaResults( SCCOL nCol, SCROW nRow, const double* pResults, size_t nLen ); - void CalculateInColumnInThread( ScInterpreterContext& rContext, SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal); - void HandleStuffAfterParallelCalculation( SCCOL nCol, SCROW nRow, size_t nLen); + void CalculateInColumnInThread( ScInterpreterContext& rContext, SCCOL nColStart, SCCOL nColEnd, + SCROW nRowStart, SCROW nRowEnd, unsigned nThisThread, unsigned nThreadsTotal); + void HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen); /** * Either start all formula cells as listeners unconditionally, or start diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx index 613107696561..c0fcc103b676 100644 --- a/sc/source/core/data/column2.cxx +++ b/sc/source/core/data/column2.cxx @@ -2930,7 +2930,8 @@ void ScColumn::SetFormulaResults( SCROW nRow, const double* pResults, size_t nLe } } -void ScColumn::CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal) +void ScColumn::CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, size_t nLen, size_t nOffset, + unsigned nThisThread, unsigned nThreadsTotal) { assert(GetDoc()->IsThreadedGroupCalcInProgress()); @@ -2953,7 +2954,7 @@ void ScColumn::CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, si for (size_t i = 0; i < nLen; ++i, ++itCell) { - if (nThreadsTotal > 0 && (i % nThreadsTotal) != nThisThread) + if (nThreadsTotal > 0 && ((i + nOffset) % nThreadsTotal) != nThisThread) continue; ScFormulaCell& rCell = **itCell; diff --git a/sc/source/core/data/documen8.cxx b/sc/source/core/data/documen8.cxx index 2c1fd4ec00bd..d7fff9711e0e 100644 --- a/sc/source/core/data/documen8.cxx +++ b/sc/source/core/data/documen8.cxx @@ -410,9 +410,9 @@ void ScDocument::SetFormulaResults( const ScAddress& rTopPos, const double* pRes pTab->SetFormulaResults(rTopPos.Col(), rTopPos.Row(), pResults, nLen); } -const ScDocumentThreadSpecific& ScDocument::CalculateInColumnInThread( ScInterpreterContext& rContext, const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal) +const ScDocumentThreadSpecific& ScDocument::CalculateInColumnInThread( ScInterpreterContext& rContext, const ScRange& rCalcRange, unsigned nThisThread, unsigned nThreadsTotal) { - ScTable* pTab = FetchTable(rTopPos.Tab()); + ScTable* pTab = FetchTable(rCalcRange.aStart.Tab()); if (!pTab) return maNonThreaded; @@ -420,7 +420,7 @@ const ScDocumentThreadSpecific& ScDocument::CalculateInColumnInThread( ScInterpr maThreadSpecific.pContext = &rContext; ScDocumentThreadSpecific::SetupFromNonThreadedData(maNonThreaded); - pTab->CalculateInColumnInThread(rContext, rTopPos.Col(), rTopPos.Row(), nLen, nThisThread, nThreadsTotal); + pTab->CalculateInColumnInThread(rContext, rCalcRange.aStart.Col(), rCalcRange.aEnd.Col(), rCalcRange.aStart.Row(), rCalcRange.aEnd.Row(), nThisThread, nThreadsTotal); assert(IsThreadedGroupCalcInProgress()); maThreadSpecific.pContext = nullptr; @@ -428,18 +428,18 @@ const ScDocumentThreadSpecific& ScDocument::CalculateInColumnInThread( ScInterpr return maThreadSpecific; } -void ScDocument::HandleStuffAfterParallelCalculation( const ScAddress& rTopPos, size_t nLen ) +void ScDocument::HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen, SCTAB nTab ) { assert(!IsThreadedGroupCalcInProgress()); for( const DelayedSetNumberFormat& data : GetNonThreadedContext().maDelayedSetNumberFormat) - SetNumberFormat( ScAddress( rTopPos.Col(), data.mRow, rTopPos.Tab()), data.mnNumberFormat ); + SetNumberFormat( ScAddress( data.mCol, data.mRow, nTab ), data.mnNumberFormat ); GetNonThreadedContext().maDelayedSetNumberFormat.clear(); - ScTable* pTab = FetchTable(rTopPos.Tab()); + ScTable* pTab = FetchTable(nTab); if (!pTab) return; - pTab->HandleStuffAfterParallelCalculation(rTopPos.Col(), rTopPos.Row(), nLen); + pTab->HandleStuffAfterParallelCalculation(nColStart, nColEnd, nRow, nLen); } void ScDocument::InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo, diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index 628e5ac3a7ba..78fd4a7a734c 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -74,6 +74,7 @@ #include #include #include +#include using namespace formula; @@ -1516,6 +1517,9 @@ bool ScFormulaCell::Interpret(SCROW nStartOffset, SCROW nEndOffset) ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper(); bool bGroupInterpreted = false; + if (mxGroup && !rRecursionHelper.CheckFGIndependence(mxGroup.get())) + return bGroupInterpreted; + static ForceCalculationType forceType = ScCalcConfig::getForceCalculationType(); TemporaryCellGroupMaker cellGroupMaker( this, forceType != ForceCalculationNone && forceType != ForceCalculationCore ); @@ -2109,7 +2113,7 @@ void ScFormulaCell::InterpretTail( ScInterpreterContext& rContext, ScInterpretTa // SetNumberFormat() is not thread-safe (modifies ScAttrArray), delay the work // to the main thread. Since thread calculations operate on formula groups, // it's enough to store just the row. - DelayedSetNumberFormat data = { aPos.Row(), nFormatIndex }; + DelayedSetNumberFormat data = { aPos.Col(), aPos.Row(), nFormatIndex }; rContext.maDelayedSetNumberFormat.push_back( data ); } bChanged = true; @@ -4606,12 +4610,20 @@ bool ScFormulaCell::InterpretFormulaGroup(SCROW nStartOffset, SCROW nEndOffset) return false; } -bool ScFormulaCell::CheckComputeDependencies(sc::FormulaLogger::GroupScope& rScope, bool fromFirstRow, SCROW nStartOffset, SCROW nEndOffset) +bool ScFormulaCell::CheckComputeDependencies(sc::FormulaLogger::GroupScope& rScope, bool fromFirstRow, + SCROW nStartOffset, SCROW nEndOffset, + bool bCalcDependencyOnly) { ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper(); // iterate over code in the formula ... // ensure all input is pre-calculated - // to avoid writing during the calculation + if (bCalcDependencyOnly) + { + ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper); + ScDependantsCalculator aCalculator(*pDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset); + return aCalculator.DoIt(); + } bool bOKToParallelize = false; { @@ -4652,6 +4664,61 @@ bool ScFormulaCell::CheckComputeDependencies(sc::FormulaLogger::GroupScope& rSco return true; } +static SCCOL lcl_probeLeftOrRightFGs(const ScFormulaCellGroupRef& xGroup, const ScDocument& rDoc, + std::unordered_set& rFGSet, + std::map& rFGMap, bool bLeft) +{ + const SCROW nLen = xGroup->mnLength; + const sal_Int32 nWt = xGroup->mnWeight; + ScAddress aAddr(xGroup->mpTopCell->aPos); + + SCCOL nColRet = aAddr.Col(); + + const SCCOL nMaxCol = rDoc.GetAllocatedColumnsCount(aAddr.Tab()) - 1; + if (bLeft) + --nColRet; + else + ++nColRet; + + while (nColRet >= 0 && nColRet <= nMaxCol) + { + aAddr.SetCol(nColRet); + const ScFormulaCell* pCell = rDoc.GetFormulaCell(aAddr); + if (!pCell) + break; + + if (!pCell->NeedsInterpret()) + break; + + const ScFormulaCellGroupRef& xNGroup = pCell->GetCellGroup(); + if (!xNGroup) + break; + + if (xNGroup->mpTopCell->aPos.Row() != aAddr.Row()) + break; + + const SCROW nNLen = xNGroup->mnLength; + const sal_Int32 nNWt = pCell->GetWeight(); + if (nNLen != nLen || nNWt != nWt) + break; + + rFGSet.insert(xNGroup.get()); + rFGMap[nColRet] = xNGroup->mpTopCell; + + if (bLeft) + --nColRet; + else + ++nColRet; + } + + if (bLeft) + ++nColRet; + else + --nColRet; + + return nColRet; +} + // To be called only from InterpretFormulaGroup(). bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope& aScope, bool& bDependencyComputed, @@ -4685,6 +4752,8 @@ bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope ScDocument* mpDocument; ScInterpreterContext* mpContext; const ScAddress& mrTopPos; + SCCOL const mnStartCol; + SCCOL const mnEndCol; SCROW const mnStartOffset; SCROW const mnEndOffset; @@ -4695,6 +4764,8 @@ bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope ScDocument* pDocument2, ScInterpreterContext* pContext, const ScAddress& rTopPos, + SCCOL nStartCol, + SCCOL nEndCol, SCROW nStartOff, SCROW nEndOff) : comphelper::ThreadTask(rTag), @@ -4703,6 +4774,8 @@ bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope mpDocument(pDocument2), mpContext(pContext), mrTopPos(rTopPos), + mnStartCol(nStartCol), + mnEndCol(nEndCol), mnStartOffset(nStartOff), mnEndOffset(nEndOff) { @@ -4710,8 +4783,9 @@ bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope virtual void doWork() override { - ScAddress aStartPos(mrTopPos.Col(), mrTopPos.Row() + mnStartOffset, mrTopPos.Tab()); - mpDocument->CalculateInColumnInThread(*mpContext, aStartPos, mnEndOffset - mnStartOffset + 1, mnThisThread, mnThreadsTotal); + ScRange aCalcRange(mnStartCol, mrTopPos.Row() + mnStartOffset, mrTopPos.Tab(), + mnEndCol, mrTopPos.Row() + mnEndOffset, mrTopPos.Tab()); + mpDocument->CalculateInColumnInThread(*mpContext, aCalcRange, mnThisThread, mnThreadsTotal); ScDocumentThreadSpecific::MergeBackIntoNonThreadedData(mpDocument->maNonThreaded); } @@ -4726,6 +4800,37 @@ bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope nThreadCount /= 2; SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads"); + + std::unordered_set aFGSet; + std::map aFGMap; + aFGSet.insert(mxGroup.get()); + + ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper(); + SCCOL nColStart = aPos.Col(); + SCCOL nColEnd = nColStart; + if (!rRecursionHelper.HasFormulaGroupSet() && pDocument->IsInDocShellRecalc()) + { + nColStart = lcl_probeLeftOrRightFGs(mxGroup, *pDocument, aFGSet, aFGMap, true); + nColEnd = lcl_probeLeftOrRightFGs(mxGroup, *pDocument, aFGSet, aFGMap, false); + } + + if (nColStart != nColEnd) + { + ScCheckIndependentFGGuard aGuard(rRecursionHelper, &aFGSet); + for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol) + { + if (nCurrCol == aPos.Col()) + continue; + + bool bFGOK = aFGMap[nCurrCol]->CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, true); + if (!bFGOK || !aGuard.AreGroupsIndependent()) + { + nColEnd = nColStart = aPos.Col(); + break; + } + } + } + { assert(!pDocument->IsThreadedGroupCalcInProgress()); pDocument->SetThreadedGroupCalcInProgress(true); @@ -4742,7 +4847,7 @@ bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i); ScDocument::SetupFromNonThreadedContext(*context, i); rThreadPool.pushTask(std::make_unique(aTag, i, nThreadCount, pDocument, context, mxGroup->mpTopCell->aPos, - nStartOffset, nEndOffset)); + nColStart, nColEnd, nStartOffset, nEndOffset)); } SAL_INFO("sc.threaded", "Waiting for threads to finish work"); @@ -4765,7 +4870,7 @@ bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope ScAddress aStartPos(mxGroup->mpTopCell->aPos); SCROW nSpanLen = nEndOffset - nStartOffset + 1; aStartPos.SetRow(aStartPos.Row() + nStartOffset); - pDocument->HandleStuffAfterParallelCalculation(aStartPos, nSpanLen); + pDocument->HandleStuffAfterParallelCalculation(nColStart, nColEnd, aStartPos.Row(), nSpanLen, aStartPos.Tab()); return true; } diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx index 27d6fb0fab22..69e0d7ac9713 100644 --- a/sc/source/core/data/table1.cxx +++ b/sc/source/core/data/table1.cxx @@ -2468,19 +2468,29 @@ void ScTable::SetFormulaResults( SCCOL nCol, SCROW nRow, const double* pResults, aCol[nCol].SetFormulaResults(nRow, pResults, nLen); } -void ScTable::CalculateInColumnInThread( ScInterpreterContext& rContext, SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal) +void ScTable::CalculateInColumnInThread( ScInterpreterContext& rContext, + SCCOL nColStart, SCCOL nColEnd, + SCROW nRowStart, SCROW nRowEnd, + unsigned nThisThread, unsigned nThreadsTotal) { - if (!ValidCol(nCol)) + if (!ValidCol(nColStart) || !ValidCol(nColEnd)) return; - aCol[nCol].CalculateInThread( rContext, nRow, nLen, nThisThread, nThreadsTotal ); + size_t nLen = nRowEnd - nRowStart + 1; + size_t nOffset = 0; + for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol) + { + aCol[nCurrCol].CalculateInThread( rContext, nRowStart, nLen, nOffset, nThisThread, nThreadsTotal ); + nOffset += nLen; + } } -void ScTable::HandleStuffAfterParallelCalculation( SCCOL nCol, SCROW nRow, size_t nLen) +void ScTable::HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen) { - assert(ValidCol(nCol)); + assert(ValidCol(nColStart) && ValidCol(nColEnd)); - aCol[nCol].HandleStuffAfterParallelCalculation( nRow, nLen ); + for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol) + aCol[nCurrCol].HandleStuffAfterParallelCalculation( nRow, nLen ); } #if DUMP_COLUMN_STORAGE diff --git a/sc/source/core/tool/recursionhelper.cxx b/sc/source/core/tool/recursionhelper.cxx index 1375048759e8..4bd819a12b75 100644 --- a/sc/source/core/tool/recursionhelper.cxx +++ b/sc/source/core/tool/recursionhelper.cxx @@ -29,6 +29,8 @@ void ScRecursionHelper::ResetIteration() ScRecursionHelper::ScRecursionHelper() { + pFGSet = nullptr; + bGroupsIndependent = true; Init(); } @@ -172,6 +174,17 @@ void ScRecursionHelper::CleanTemporaryGroupCells() } } +bool ScRecursionHelper::CheckFGIndependence(ScFormulaCellGroup* pFG) +{ + if (pFGSet && pFGSet->count(pFG)) + { + bGroupsIndependent = false; + return false; + } + + return true; +} + ScFormulaGroupCycleCheckGuard::ScFormulaGroupCycleCheckGuard(ScRecursionHelper& rRecursionHelper, ScFormulaCell* pCell) : mrRecHelper(rRecursionHelper) { @@ -201,4 +214,35 @@ ScFormulaGroupDependencyComputeGuard::~ScFormulaGroupDependencyComputeGuard() mrRecHelper.DecDepComputeLevel(); } +ScCheckIndependentFGGuard::ScCheckIndependentFGGuard(ScRecursionHelper& rRecursionHelper, + std::unordered_set* pSet) : + mrRecHelper(rRecursionHelper), + mbUsedFGSet(false) +{ + if (!mrRecHelper.HasFormulaGroupSet()) + { + mrRecHelper.SetFormulaGroupSet(pSet); + mrRecHelper.SetGroupsIndependent(true); + mbUsedFGSet = true; + } +} + +ScCheckIndependentFGGuard::~ScCheckIndependentFGGuard() +{ + if (mbUsedFGSet) + { + // Reset to defaults. + mrRecHelper.SetFormulaGroupSet(nullptr); + mrRecHelper.SetGroupsIndependent(true); + } +} + +bool ScCheckIndependentFGGuard::AreGroupsIndependent() +{ + if (!mbUsedFGSet) + return false; + + return mrRecHelper.AreGroupsIndependent(); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3