summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Lillqvist <tml@collabora.com>2017-08-16 12:21:53 +0300
committerDennis Francis <dennis.francis@collabora.co.uk>2017-11-21 16:09:40 +0530
commit99a36cb35c92e3e6b4ff2db257df221cb74c9eae (patch)
tree5d06a7a170b883702dd3f481d62fe36462dc6291
parenta60ffa6985dec32cc73032a7774f58694a6ee9a7 (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.dox2
-rw-r--r--sc/inc/column.hxx3
-rw-r--r--sc/inc/document.hxx5
-rw-r--r--sc/inc/formulacell.hxx17
-rw-r--r--sc/inc/table.hxx3
-rw-r--r--sc/source/core/data/column2.cxx68
-rw-r--r--sc/source/core/data/documen8.cxx9
-rw-r--r--sc/source/core/data/document.cxx9
-rw-r--r--sc/source/core/data/formulacell.cxx219
-rw-r--r--sc/source/core/data/table1.cxx19
-rw-r--r--sc/source/core/tool/token.cxx3
-rw-r--r--sc/source/ui/docshell/docsh4.cxx3
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 )