diff options
author | Tor Lillqvist <tml@collabora.com> | 2017-08-16 12:21:53 +0300 |
---|---|---|
committer | Dennis Francis <dennis.francis@collabora.co.uk> | 2017-11-21 16:09:40 +0530 |
commit | 99a36cb35c92e3e6b4ff2db257df221cb74c9eae (patch) | |
tree | 5d06a7a170b883702dd3f481d62fe36462dc6291 | |
parent | a60ffa6985dec32cc73032a7774f58694a6ee9a7 (diff) |
First steps for Calc parallelism
For now, formula group calculations are done in parallel threads when
1) OpenCL is not used, and 2) the environment variable
CPU_THREADED_CALCULATION is set. This commit is a surely broken first
step and does not actually work that well at all.
Change-Id: Ia7e5019703ba89bff0695faef0f7504765061149
-rw-r--r-- | include/sal/log-areas.dox | 2 | ||||
-rw-r--r-- | sc/inc/column.hxx | 3 | ||||
-rw-r--r-- | sc/inc/document.hxx | 5 | ||||
-rw-r--r-- | sc/inc/formulacell.hxx | 17 | ||||
-rw-r--r-- | sc/inc/table.hxx | 3 | ||||
-rw-r--r-- | sc/source/core/data/column2.cxx | 68 | ||||
-rw-r--r-- | sc/source/core/data/documen8.cxx | 9 | ||||
-rw-r--r-- | sc/source/core/data/document.cxx | 9 | ||||
-rw-r--r-- | sc/source/core/data/formulacell.cxx | 219 | ||||
-rw-r--r-- | sc/source/core/data/table1.cxx | 19 | ||||
-rw-r--r-- | sc/source/core/tool/token.cxx | 3 | ||||
-rw-r--r-- | sc/source/ui/docshell/docsh4.cxx | 3 |
12 files changed, 339 insertions, 21 deletions
diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox index dac4e7312efd..5ca9cf7b8986 100644 --- a/include/sal/log-areas.dox +++ b/include/sal/log-areas.dox @@ -150,6 +150,8 @@ certain functionality. @li @c sc.orcus.style @li @c sc.orcus.table @li @c sc.qa +@li @c sc.threaded +@li @c sc.timing @li @c sc.ui - Calc UI @li @c sc.uitest - Calc UI Test part @li @c sc.viewdata diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx index 6d2ac9e44cf1..e6ddccb1c5eb 100644 --- a/sc/inc/column.hxx +++ b/sc/inc/column.hxx @@ -578,9 +578,12 @@ public: bool ResolveStaticReference( ScMatrix& rMat, SCCOL nMatCol, SCROW nRow1, SCROW nRow2 ); void FillMatrix( ScMatrix& rMat, size_t nMatCol, SCROW nRow1, SCROW nRow2, svl::SharedStringPool* pPool ) const; formula::VectorRefArray FetchVectorRefArray( SCROW nRow1, SCROW nRow2 ); + bool HandleRefArrayForParallelism( SCROW nRow1, SCROW nRow2 ); void SetFormulaResults( SCROW nRow, const double* pResults, size_t nLen ); void SetFormulaResults( SCROW nRow, const formula::FormulaConstTokenRef* pResults, size_t nLen ); + void CalculateInThread( SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal); + void SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat ); SvtBroadcaster* GetBroadcaster( SCROW nRow ); diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index dec590867b13..e1ba0f33f30a 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -2008,6 +2008,8 @@ public: void SC_DLLPUBLIC SetFormulaResults( const ScAddress& rTopPos, const double* pResults, size_t nLen ); void SC_DLLPUBLIC SetFormulaResults( const ScAddress& rTopPos, const formula::FormulaConstTokenRef* pResults, size_t nLen ); + void CalculateInColumnInThread( const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal); + /** * Transfer a series of contiguous cell values from specified position to * the passed container. The specified segment will become empty after the @@ -2296,6 +2298,7 @@ public: formula::FormulaTokenRef ResolveStaticReference( const ScRange& rRange ); formula::VectorRefArray FetchVectorRefArray( const ScAddress& rPos, SCROW nLength ); + bool HandleRefArrayForParallelism( const ScAddress& rPos, SCROW nLength ); /** * Call this before any operations that might trigger one or more formula @@ -2335,6 +2338,7 @@ public: void ConvertFormulaToValue( const ScRange& rRange, sc::TableValues* pUndo ); void SwapNonEmpty( sc::TableValues& rValues ); void finalizeOutlineImport(); + bool TableExists( SCTAB nTab ) const; SC_DLLPUBLIC ScColumnsRange GetColumnsRange(SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd) const; @@ -2355,7 +2359,6 @@ private: ScDocument* mpDoc; }; - bool TableExists( SCTAB nTab ) const; ScTable* FetchTable( SCTAB nTab ); const ScTable* FetchTable( SCTAB nTab ) const; diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx index 17db673be6fe..3eed0ea45246 100644 --- a/sc/inc/formulacell.hxx +++ b/sc/inc/formulacell.hxx @@ -134,14 +134,6 @@ private: bool mbPostponedDirty : 1; // if cell needs to be set dirty later bool mbIsExtRef : 1; // has references in ScExternalRefManager; never cleared after set - enum ScInterpretTailParameter - { - SCITP_NORMAL, - SCITP_FROM_ITERATION, - SCITP_CLOSE_ITERATION_CIRCLE - }; - void InterpretTail( ScInterpretTailParameter ); - /** * Update reference in response to cell copy-n-paste. */ @@ -151,6 +143,15 @@ private: ScFormulaCell( const ScFormulaCell& ) = delete; public: + + enum ScInterpretTailParameter + { + SCITP_NORMAL, + SCITP_FROM_ITERATION, + SCITP_CLOSE_ITERATION_CIRCLE + }; + void InterpretTail( ScInterpretTailParameter, bool bUpdateProgress ); + enum CompareState { NotEqual = 0, EqualInvariant, EqualRelativeRef }; DECL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell ) diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index 1ed1eaadb71b..c7fa43e64e96 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -978,6 +978,7 @@ public: formula::FormulaTokenRef ResolveStaticReference( SCCOL nCol, SCROW nRow ); formula::FormulaTokenRef ResolveStaticReference( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ); formula::VectorRefArray FetchVectorRefArray( SCCOL nCol, SCROW nRow1, SCROW nRow2 ); + bool HandleRefArrayForParallelism( SCCOL nCol, SCROW nRow1, SCROW nRow2 ); void SplitFormulaGroups( SCCOL nCol, std::vector<SCROW>& rRows ); void UnshareFormulaCells( SCCOL nCol, std::vector<SCROW>& rRows ); @@ -996,6 +997,8 @@ public: void SetFormulaResults( SCCOL nCol, SCROW nRow, const double* pResults, size_t nLen ); void SetFormulaResults( SCCOL nCol, SCROW nRow, const formula::FormulaConstTokenRef* pResults, size_t nLen ); + void CalculateInColumnInThread( SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal); + /** * Either start all formula cells as listeners unconditionally, or start * those that are marked "needs listening". diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx index 0fa0f3d1358b..8e852f856c7a 100644 --- a/sc/source/core/data/column2.cxx +++ b/sc/source/core/data/column2.cxx @@ -2614,6 +2614,27 @@ copyFirstFormulaBlock( return rCxt.setCachedColArray(nTab, nCol, pNumArray, pStrArray); } +bool +DoFormulaBlockForParallelism( const sc::CellStoreType::iterator& itBlk ) +{ + sc::formula_block::iterator it = sc::formula_block::begin(*itBlk->data); + sc::formula_block::iterator itEnd; + + itEnd = it; + std::advance(itEnd, itBlk->size); + for (; it != itEnd; ++it) + { + ScFormulaCell& rFC = **it; + sc::FormulaResultValue aRes = rFC.GetResult(); + if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError != FormulaError::NONE) + { + return false; + } + } + + return true; +} + struct NonNullStringFinder { bool operator() (const rtl_uString* p) const { return p != nullptr; } @@ -2806,6 +2827,27 @@ formula::VectorRefArray ScColumn::FetchVectorRefArray( SCROW nRow1, SCROW nRow2 return formula::VectorRefArray(formula::VectorRefArray::Invalid); } +bool ScColumn::HandleRefArrayForParallelism( SCROW nRow1, SCROW nRow2 ) +{ + if (nRow1 > nRow2) + return false; + + for (auto itBlk = maCells.begin(); itBlk != maCells.end(); ++itBlk) + { + switch (itBlk->type) + { + case sc::element_type_formula: + { + if (!DoFormulaBlockForParallelism( itBlk)) + return false; + return true; + } + } + } + + return true; +} + void ScColumn::SetFormulaResults( SCROW nRow, const double* pResults, size_t nLen ) { sc::CellStoreType::position_type aPos = maCells.position(nRow); @@ -2862,6 +2904,32 @@ void ScColumn::SetFormulaResults( SCROW nRow, const formula::FormulaConstTokenRe } } +void ScColumn::CalculateInThread( SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal) +{ + sc::CellStoreType::position_type aPos = maCells.position(nRow); + sc::CellStoreType::iterator it = aPos.first; + if (it->type != sc::element_type_formula) + // This is not a formula block. + return; + + size_t nBlockLen = it->size - aPos.second; + if (nBlockLen < nLen) + // Length is longer than the length of formula cells. Not good. + return; + + sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data); + std::advance(itCell, aPos.second); + + for (size_t i = 0; i < nLen; ++i, ++itCell) + { + if (nThreadsTotal > 0 && (i % nThreadsTotal) != nThisThread) + continue; + + ScFormulaCell& rCell = **itCell; + rCell.InterpretTail(ScFormulaCell::SCITP_NORMAL, false); + } +} + void ScColumn::SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat ) { ApplyAttr(nRow, SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat)); diff --git a/sc/source/core/data/documen8.cxx b/sc/source/core/data/documen8.cxx index c0ee2e8f1b1b..ea5c34b55bc9 100644 --- a/sc/source/core/data/documen8.cxx +++ b/sc/source/core/data/documen8.cxx @@ -427,6 +427,15 @@ void ScDocument::SetFormulaResults( pTab->SetFormulaResults(rTopPos.Col(), rTopPos.Row(), pResults, nLen); } +void ScDocument::CalculateInColumnInThread( const ScAddress& rTopPos, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal) +{ + ScTable* pTab = FetchTable(rTopPos.Tab()); + if (!pTab) + return; + + pTab->CalculateInColumnInThread(rTopPos.Col(), rTopPos.Row(), nLen, nThisThread, nThreadsTotal); +} + void ScDocument::InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo, bool bNumFormatChanged ) { diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx index 2f9791dc696e..a2f98bb17c7e 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -1808,6 +1808,15 @@ void ScDocument::UnlockAdjustHeight() --nAdjustHeightLock; } +bool ScDocument::HandleRefArrayForParallelism( const ScAddress& rPos, SCROW nLength ) +{ + SCTAB nTab = rPos.Tab(); + if (!TableExists(nTab)) + return false; + + return maTabs[nTab]->HandleRefArrayForParallelism(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1); +} + bool ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew ) { if ( rOld == rNew ) diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index 3dea10f5b6eb..bda1c3cfa07d 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -17,6 +17,8 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include <config_features.h> + #include <sal/config.h> #include <cassert> @@ -45,7 +47,7 @@ #include <chgtrack.hxx> #include <tokenarray.hxx> -#include <config_features.h> +#include <comphelper/threadpool.hxx> #include <formula/errorcodes.hxx> #include <formula/vectortoken.hxx> #include <svl/intitem.hxx> @@ -1524,10 +1526,10 @@ void ScFormulaCell::Interpret() bool bGroupInterpreted = InterpretFormulaGroup(); aDC.leaveGroup(); if (!bGroupInterpreted) - InterpretTail( SCITP_NORMAL); + InterpretTail( SCITP_NORMAL, true); #else if (!InterpretFormulaGroup()) - InterpretTail( SCITP_NORMAL); + InterpretTail( SCITP_NORMAL, true); #endif } @@ -1590,7 +1592,7 @@ void ScFormulaCell::Interpret() bResumeIteration = false; // Close circle once. rRecursionHelper.GetList().back().pCell->InterpretTail( - SCITP_CLOSE_ITERATION_CIRCLE); + SCITP_CLOSE_ITERATION_CIRCLE, true); // Start at 1, init things. rRecursionHelper.StartIteration(); // Mark all cells being in iteration. @@ -1619,7 +1621,7 @@ void ScFormulaCell::Interpret() pIterCell->GetSeenInIteration()) { (*aIter).aPreviousResult = pIterCell->aResult; - pIterCell->InterpretTail( SCITP_FROM_ITERATION); + pIterCell->InterpretTail( SCITP_FROM_ITERATION, true); } rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty(); } @@ -1689,7 +1691,7 @@ void ScFormulaCell::Interpret() ScFormulaCell* pCell = (*aIter).pCell; if (pCell->IsDirtyOrInTableOpDirty()) { - pCell->InterpretTail( SCITP_NORMAL); + pCell->InterpretTail( SCITP_NORMAL, true); if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell()) pCell->bRunning = (*aIter).bOldRunning; } @@ -1722,7 +1724,7 @@ void ScFormulaCell::Interpret() #endif } -void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam ) +void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam, bool bUpdateProgress ) { RecursionCounter aRecursionCounter( pDocument->GetRecursionHelper(), this); nSeenInIteration = pDocument->GetRecursionHelper().GetIteration(); @@ -1742,7 +1744,6 @@ void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam ) pCode->SetCodeError( FormulaError::NoCode ); // This is worth an assertion; if encountered in daily work // documents we might need another solution. Or just confirm correctness. - OSL_FAIL( "ScFormulaCell::Interpret: no RPN, no error, no token, but hybrid formula string" ); return; } CompileTokenArray(); @@ -2103,11 +2104,14 @@ void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam ) } // Reschedule slows the whole thing down considerably, thus only execute on percent change - ScProgress *pProgress = ScProgress::GetInterpretProgress(); - if (pProgress && pProgress->Enabled()) + if (bUpdateProgress) { - pProgress->SetStateCountDownOnPercent( - pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE ); + ScProgress *pProgress = ScProgress::GetInterpretProgress(); + if (pProgress && pProgress->Enabled()) + { + pProgress->SetStateCountDownOnPercent( + pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE ); + } } switch (pInterpreter->GetVolatileType()) @@ -4045,6 +4049,113 @@ int splitup(int N, int K, int& A) } // anonymous namespace +struct ScDependantsCalculator +{ + ScDocument& mrDoc; + ScTokenArray& mrCode; + ScFormulaCell& mrCell; + const ScAddress& mrPos; + + ScDependantsCalculator(ScDocument& rDoc, ScTokenArray& rCode, ScFormulaCell& rCell, const ScAddress& rPos) : + mrDoc(rDoc), + mrCode(rCode), + mrCell(rCell), + mrPos(rPos) + { + } + + // FIXME: copy-pasted from ScGroupTokenConverter. factor out somewhere else + bool cellIsSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow) + { + if (rRefPos.Col() != mrPos.Col()) + return false; + + SCROW nLen = mrCell.GetCellGroup()->mnLength; + SCROW nEndRow = mrPos.Row() + nLen - 1; + + if (nRelRow < 0) + { + SCROW nTest = nEndRow; + nTest += nRelRow; + if (nTest >= mrPos.Row()) + return true; + } + else if (nRelRow > 0) + { + SCROW nTest = mrPos.Row(); // top row. + nTest += nRelRow; + if (nTest <= nEndRow) + return true; + } + + return false; + } + + // FIXME: another copy-paste + SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen) + { + SCROW nLastRow = nRow + nRowLen - 1; // current last row. + nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow); + if (nLastRow < (nRow + nRowLen - 1)) + { + // This can end up negative! Was that the original intent, or + // is it accidental? Was it not like that originally but the + // surrounding conditions changed? + nRowLen = nLastRow - nRow + 1; + // Anyway, let's assume it doesn't make sense to return a + // negative value here. But should we then return 0 or 1? In + // the "Column is empty" case below, we return 1, why!? And, + // at the callsites there are tests for a zero value returned + // from this function (but not for a negative one). + if (nRowLen < 0) + nRowLen = 0; + } + else if (nLastRow == 0) + // Column is empty. + nRowLen = 1; + + return nRowLen; + } + + bool DoIt() + { +// from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx + for (auto p: mrCode.Tokens()) + { + SCROW nLen = mrCell.GetCellGroup()->mnLength; + switch (p->GetType()) + { + case svSingleRef: + { + ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1 + ScAddress aRefPos = aRef.toAbs(mrPos); + if (aRef.IsRowRel()) + { + if (cellIsSelfReferenceRelative(aRefPos, aRef.Row())) + return false; + + // Trim data array length to actual data range. + SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row(), nLen); + // Fetch double array guarantees that the length of the + // returned array equals or greater than the requested + // length. + + if (mrDoc.TableExists(aRefPos.Tab()) && nTrimLen) + { + if (!mrDoc.HandleRefArrayForParallelism(aRefPos, nTrimLen)) + return false; + } + } + } + break; + default: + break; + } + } + return true; + } +}; + bool ScFormulaCell::InterpretFormulaGroup() { if (!mxGroup || !pCode) @@ -4081,6 +4192,90 @@ bool ScFormulaCell::InterpretFormulaGroup() return false; } + if (!ScCalcConfig::isOpenCLEnabled() && std::getenv("CPU_THREADED_CALCULATION")) + { + // iterate over code in the formula ... + // ensure all input is pre-calculated - + // to avoid writing during the calculation + ScDependantsCalculator aCalculator(*pDocument, *pCode, *this, mxGroup->mpTopCell->aPos); + + // Disable or hugely enlarge subset for S/W group + // threading interpreter + + if (!aCalculator.DoIt()) + { + mxGroup->meCalcState = sc::GroupCalcDisabled; + aScope.addMessage("could not do new dependencies calculation thing"); + return false; + } + + // Then do the threaded calculation + + bool result = true; + class Executor : public comphelper::ThreadTask + { + private: + const unsigned mnThisThread; + const unsigned mnThreadsTotal; + ScDocument* mpDocument; + const ScAddress& mrTopPos; + SCROW mnLength; + std::vector<int>& mrResult; + + public: + Executor(std::shared_ptr<comphelper::ThreadTaskTag>& rTag, + unsigned nThisThread, + unsigned nThreadsTotal, + ScDocument* pDocument2, + const ScAddress& rTopPos, + SCROW nLength, + std::vector<int>& rResult) : + comphelper::ThreadTask(rTag), + mnThisThread(nThisThread), + mnThreadsTotal(nThreadsTotal), + mpDocument(pDocument2), + mrTopPos(rTopPos), + mnLength(nLength), + mrResult(rResult) + { + } + + virtual void doWork() override + { + mpDocument->CalculateInColumnInThread(mrTopPos, mnLength, mnThisThread, mnThreadsTotal); + // FIXME: How to determine whether it "worked" or not? Does it even have a meaning? Just + // drop this as YAGNI? + mrResult[mnThisThread] = static_cast<int>(true); + } + + }; + + comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool()); + sal_Int32 nThreadCount = rThreadPool.getWorkerCount(); + + SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads"); + // Start nThreadCount new threads + std::vector<int> vResult(nThreadCount); + std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag(); + for (int i = 0; i < nThreadCount; ++i) + { + rThreadPool.pushTask(new Executor(aTag, i, nThreadCount, pDocument, mxGroup->mpTopCell->aPos, mxGroup->mnLength, vResult)); + } + SAL_INFO("sc.threaded", "Joining threads"); + rThreadPool.waitUntilDone(aTag); + SAL_INFO("sc.threaded", "Done"); + for (int i = 0; i < nThreadCount; ++i) + { + if (!vResult[i]) + { + SAL_INFO("sc.threaded", "Thread " << i << " failed"); + result = false; + } + } + + return result; + } + switch (pCode->GetVectorState()) { case FormulaVectorEnabled: diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx index 8c9f30729de0..fdafbbc6c1f8 100644 --- a/sc/source/core/data/table1.cxx +++ b/sc/source/core/data/table1.cxx @@ -2272,6 +2272,17 @@ formula::VectorRefArray ScTable::FetchVectorRefArray( SCCOL nCol, SCROW nRow1, S return aCol[nCol].FetchVectorRefArray(nRow1, nRow2); } +bool ScTable::HandleRefArrayForParallelism( SCCOL nCol, SCROW nRow1, SCROW nRow2 ) +{ + if (nRow2 < nRow1) + return false; + + if ( !IsColValid( nCol ) || !ValidRow( nRow1 ) || !ValidRow( nRow2 ) ) + return false; + + return aCol[nCol].HandleRefArrayForParallelism(nRow1, nRow2); +} + ScRefCellValue ScTable::GetRefCellValue( SCCOL nCol, SCROW nRow ) { if ( !IsColRowValid( nCol, nRow ) ) @@ -2327,6 +2338,14 @@ void ScTable::SetFormulaResults( aCol[nCol].SetFormulaResults(nRow, pResults, nLen); } +void ScTable::CalculateInColumnInThread( SCCOL nCol, SCROW nRow, size_t nLen, unsigned nThisThread, unsigned nThreadsTotal) +{ + if (!ValidCol(nCol)) + return; + + aCol[nCol].CalculateInThread( nRow, nLen, nThisThread, nThreadsTotal ); +} + #if DUMP_COLUMN_STORAGE void ScTable::DumpColumnStorage( SCCOL nCol ) const { diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx index 696bbe8705cb..0cf7c872f031 100644 --- a/sc/source/core/tool/token.cxx +++ b/sc/source/core/tool/token.cxx @@ -1341,6 +1341,9 @@ void ScTokenArray::CheckToken( const FormulaToken& r ) // It's already disabled. No more checking needed. return; + if (!ScCalcConfig::isOpenCLEnabled() && std::getenv("CPU_THREADED_CALCULATION")) + return; + OpCode eOp = r.GetOpCode(); if (SC_OPCODE_START_FUNCTION <= eOp && eOp < SC_OPCODE_STOP_FUNCTION) diff --git a/sc/source/ui/docshell/docsh4.cxx b/sc/source/ui/docshell/docsh4.cxx index e5620e8dd74a..60beffb4bfff 100644 --- a/sc/source/ui/docshell/docsh4.cxx +++ b/sc/source/ui/docshell/docsh4.cxx @@ -1337,6 +1337,7 @@ void ScDocShell::DoRecalc( bool bApi ) void ScDocShell::DoHardRecalc() { + auto start = std::chrono::steady_clock::now(); WaitObject aWaitObj( GetActiveDialogParent() ); ScTabViewShell* pSh = GetBestViewShell(); if ( pSh ) @@ -1367,6 +1368,8 @@ void ScDocShell::DoHardRecalc() aDocument.SetStreamValid(nTab, false); PostPaintGridAll(); + auto end = std::chrono::steady_clock::now(); + SAL_INFO("sc.timing", "ScDocShell::DoHardRecalc(): took " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms"); } void ScDocShell::DoAutoStyle( const ScRange& rRange, const OUString& rStyle ) |