[Libreoffice-commits] core.git: Branch 'feature/calc-parallel' - 5 commits - include/tools sal/rtl sc/inc sc/source tools/source

Dennis Francis dennis.francis at collabora.co.uk
Wed Nov 15 12:27:42 UTC 2017


 include/tools/cpuid.hxx              |    1 
 sal/rtl/alloc_global.cxx             |    2 
 sc/inc/tokenarray.hxx                |    1 
 sc/inc/types.hxx                     |    1 
 sc/source/core/data/formulacell.cxx  |   27 ++-
 sc/source/core/tool/formulagroup.cxx |  299 ++++++++++++++++++++++++-----------
 sc/source/core/tool/token.cxx        |   31 ++-
 tools/source/misc/cpuid.cxx          |   11 +
 8 files changed, 268 insertions(+), 105 deletions(-)

New commits:
commit 8d76f0b2e9eb528101cf3221d13de69dd880c72f
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 15 17:09:08 2017 +0530

    halve thread count if HT active for group interpreter too
    
    Change-Id: Iacc93122191152183127500a4172358a14e96c8b

diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 1159814783d2..199996478302 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -49,6 +49,7 @@
 #include <tokenarray.hxx>
 
 #include <comphelper/threadpool.hxx>
+#include <tools/cpuid.hxx>
 #include <formula/errorcodes.hxx>
 #include <formula/vectortoken.hxx>
 #include <svl/intitem.hxx>
@@ -4364,6 +4365,8 @@ bool ScFormulaCell::InterpretFormulaGroup()
             return false;
         }
 
+        static bool bHyperThreadingActive = tools::cpuid::hasHyperThreading();
+
         // Then do the threaded calculation
 
         class Executor : public comphelper::ThreadTask
@@ -4411,6 +4414,9 @@ bool ScFormulaCell::InterpretFormulaGroup()
         comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
         sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
 
+        if ( bHyperThreadingActive && nThreadCount >= 2 )
+            nThreadCount /= 2;
+
         SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
 
         {
commit c0f596e5e0f9589e70052a95a90f88e8e2023532
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 15 16:52:44 2017 +0530

    Disable custom allocator
    
    This has big positive effect on software interpreter threading
    performance scaling.
    
    Change-Id: I8fbb6bf8f7ed410fd53278acee63bf65f13bac38

diff --git a/sal/rtl/alloc_global.cxx b/sal/rtl/alloc_global.cxx
index 3d74287bc7e5..47643f3ea74a 100644
--- a/sal/rtl/alloc_global.cxx
+++ b/sal/rtl/alloc_global.cxx
@@ -35,7 +35,7 @@ AllocMode alloc_mode = AllocMode::UNSET;
 static void determine_alloc_mode()
 {
     assert(alloc_mode == AllocMode::UNSET);
-    alloc_mode = (getenv("G_SLICE") == nullptr ? AllocMode::CUSTOM : AllocMode::SYSTEM);
+    alloc_mode = AllocMode::SYSTEM;
 }
 
 static const sal_Size g_alloc_sizes[] =
commit 02af02d4f576ead19633e52c83463f5bdea8ea9b
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 15 16:37:45 2017 +0530

    halve the number of threads if HT is active
    
    added hasHyperThreading() function to tools::cpuid
    to detect hyperthreading.
    
    Change-Id: I13fab4b6c649e681c329b7e3f4c9f36bda879d84

diff --git a/include/tools/cpuid.hxx b/include/tools/cpuid.hxx
index d7aa07d54258..419d05714ae4 100644
--- a/include/tools/cpuid.hxx
+++ b/include/tools/cpuid.hxx
@@ -25,6 +25,7 @@ namespace tools
 namespace cpuid
 {
     TOOLS_DLLPUBLIC bool hasSSE2();
+    TOOLS_DLLPUBLIC bool hasHyperThreading();
 }
 }
 
diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index 7be682322221..e77554d89805 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -19,6 +19,7 @@
 #include <scmatrix.hxx>
 #include <globalnames.hxx>
 #include <comphelper/threadpool.hxx>
+#include <tools/cpuid.hxx>
 
 #include <formula/vectortoken.hxx>
 #include <officecfg/Office/Common.hxx>
@@ -299,6 +300,7 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
     // The caller must ensure that the top position is the start position of
     // the group.
 
+    static bool bHyperThreadingActive = tools::cpuid::hasHyperThreading();
     ScAddress aTmpPos = rTopPos;
     std::vector<formula::FormulaConstTokenRef> aResults(xGroup->mnLength);
 
@@ -335,6 +337,9 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
         comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
         sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
 
+        if ( bHyperThreadingActive && nThreadCount >= 2 )
+            nThreadCount /= 2;
+
         SCROW nLen = xGroup->mnLength;
         SCROW nBatchSize = nLen / nThreadCount;
         if (nLen < nThreadCount)
diff --git a/tools/source/misc/cpuid.cxx b/tools/source/misc/cpuid.cxx
index 1518dfc175c4..e3ba82dffda5 100644
--- a/tools/source/misc/cpuid.cxx
+++ b/tools/source/misc/cpuid.cxx
@@ -16,8 +16,6 @@ namespace tools
 namespace cpuid
 {
 
-#if defined(LO_SSE2_AVAILABLE)
-
 namespace
 {
 #if defined(_MSC_VER)
@@ -35,6 +33,8 @@ void getCpuId(uint32_t array[4])
 #endif
 }
 
+#if defined(LO_SSE2_AVAILABLE)
+
 bool hasSSE2()
 {
     uint32_t cpuInfoArray[] = {0, 0, 0, 0};
@@ -48,6 +48,13 @@ bool hasSSE2() { return false; }
 
 #endif
 
+bool hasHyperThreading()
+{
+    uint32_t cpuInfoArray[] = {0, 0, 0, 0};
+    getCpuId(cpuInfoArray);
+    return (cpuInfoArray[3] & (1 << 28)) != 0;
+}
+
 }
 }
 
commit 198ebcdc33de035f3f1a5d8cec7e7c50e7ef6a1e
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 15 16:14:32 2017 +0530

    Avoid ScTokenArray thrash
    
    Allocate ScTokenArray object only once per worker thread, fill it
    for the first row/cell and reuse them for subsequent rows/cells
    if possible.
    
    Change-Id: If8f20da618938d0e189224f189b4763815702e10

diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index e22e294b9262..7be682322221 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -171,12 +171,14 @@ public:
     {
         double fNan;
         rtl::math::setNan(&fNan);
+        ScTokenArray aCode2;
         for (SCROW i = mnIdx; i <= mnLastIdx; ++i, maBatchTopPos.IncRow())
         {
-            ScTokenArray aCode2;
             formula::FormulaTokenArrayPlainIterator aIter(mrCode);
-            for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+            size_t nTokIdx = 0;
+            for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next(), ++nTokIdx)
             {
+                formula::FormulaToken* pTargetTok = aCode2.TokenAt(nTokIdx);
                 switch (p->GetType())
                 {
                     case formula::svSingleVectorRef:
@@ -200,14 +202,25 @@ public:
                         {
                             // This is a string cell.
                             svl::SharedStringPool& rPool = mrDoc.GetSharedStringPool();
-                            aCode2.AddString(rPool.intern(OUString(pStr)));
+                            if ( !pTargetTok )
+                                aCode2.AddString(rPool.intern(OUString(pStr)));
+                            else
+                                pTargetTok->SetString(rPool.intern(OUString(pStr)));
                         }
                         else if (rtl::math::isNan(fVal))
+                        {
                             // Value of NaN represents an empty cell.
-                            aCode2.AddToken(ScEmptyCellToken(false, false));
+                            if ( !pTargetTok )
+                                aCode2.AddToken(ScEmptyCellToken(false, false));
+                        }
                         else
+                        {
                             // Numeric cell.
-                            aCode2.AddDouble(fVal);
+                            if ( !pTargetTok )
+                                aCode2.AddDouble(fVal);
+                            else
+                                pTargetTok->GetDoubleAsReference() = fVal;
+                        }
                     }
                     break;
                     case formula::svDoubleVectorRef:
@@ -229,17 +242,29 @@ public:
                             aRefRange.aEnd.SetRow(mrTopPos.Row() + nRowEnd);
                             aRef.InitRange(aRefRange);
                             formula::FormulaTokenRef xTok(new ScMatrixRangeToken(pMat, aRef));
-                            aCode2.AddToken(*xTok);
+                            if ( !pTargetTok )
+                                aCode2.AddToken(*xTok);
+                            else
+                                aCode2.ReplaceToken(nTokIdx, xTok.get(), formula::FormulaTokenArray::CODE_ONLY);
                         }
                         else
                         {
-                            ScMatrixToken aTok(pMat);
-                            aCode2.AddToken(aTok);
+                            if ( !pTargetTok )
+                            {
+                                ScMatrixToken aTok(pMat);
+                                aCode2.AddToken(aTok);
+                            }
+                            else
+                            {
+                                ScMatrixToken* pMatTok = new ScMatrixToken(pMat);
+                                aCode2.ReplaceToken(nTokIdx, pMatTok, formula::FormulaTokenArray::CODE_ONLY);
+                            }
                         }
                     }
                     break;
                     default:
-                        aCode2.AddToken(*p);
+                        if ( !pTargetTok )
+                            aCode2.AddToken(*p);
                 } // end of switch statement
             } // end of formula token for loop
 
commit 12fdb96cec0d9d06a60ce0641386108401eaf367
Author: Dennis Francis <dennis.francis at collabora.co.uk>
Date:   Wed Nov 1 13:45:09 2017 +0530

    Thread the software interpreter
    
    Also introduce new state
    
    ScFormulaVectorState::FormulaVectorEnabledForThreading
    
    to indicate that using the “traditional” vectoring is disabled,
    but threading should be tried.
    
    Change-Id: I552d9e29e1ab9e5721534e07f4a45fdd5a23f399

diff --git a/sc/inc/tokenarray.hxx b/sc/inc/tokenarray.hxx
index 294794f959e0..8b352b447f09 100644
--- a/sc/inc/tokenarray.hxx
+++ b/sc/inc/tokenarray.hxx
@@ -92,6 +92,7 @@ public:
         svl::SharedStringPool& rSPool,
         formula::ExternalReferenceHelper* _pRef) override;
     virtual void CheckToken( const formula::FormulaToken& r ) override;
+    void CheckForThreading( OpCode eOp );
     virtual formula::FormulaToken* AddOpCode( OpCode eCode ) override;
     /** ScSingleRefToken with ocPush. */
     formula::FormulaToken* AddSingleReference( const ScSingleRefData& rRef );
diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx
index 51898c291fde..cc82e363765d 100644
--- a/sc/inc/types.hxx
+++ b/sc/inc/types.hxx
@@ -59,6 +59,7 @@ enum ScFormulaVectorState
 
     FormulaVectorEnabled,
     FormulaVectorCheckReference,
+    FormulaVectorEnabledForThreading,
     FormulaVectorUnknown
 };
 
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 61b0f5a87702..1159814783d2 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -4345,7 +4345,9 @@ bool ScFormulaCell::InterpretFormulaGroup()
         return false;
     }
 
-    if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() && pCode->GetVectorState() != FormulaVectorDisabledNotInSubSet && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
+    if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() &&
+        pCode->GetVectorState() == FormulaVectorEnabledForThreading &&
+        officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
     {
         // iterate over code in the formula ...
         // ensure all input is pre-calculated -
@@ -4437,26 +4439,30 @@ bool ScFormulaCell::InterpretFormulaGroup()
         return true;
     }
 
+    bool bCanVectorize = false;
     switch (pCode->GetVectorState())
     {
         case FormulaVectorEnabled:
         case FormulaVectorCheckReference:
-            // Good.
+            bCanVectorize = true; // Good.
         break;
 
         // Not good.
         case FormulaVectorDisabledByOpCode:
             aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
-            return false;
+            break;
         case FormulaVectorDisabledNotInSoftwareSubset:
             aScope.addMessage("group calc disabled due to vector state (opcode not in software subset)");
-            return false;
+            break;
         case FormulaVectorDisabledByStackVariable:
             aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
-            return false;
+            break;
         case FormulaVectorDisabledNotInSubSet:
             aScope.addMessage("group calc disabled due to vector state (opcode not in subset)");
-            return false;
+            break;
+        case FormulaVectorEnabledForThreading:
+            aScope.addMessage("group calc disabled due to vector state (wanted to try threading but couldn't)");
+            break;
         case FormulaVectorDisabled:
         case FormulaVectorUnknown:
         default:
@@ -4464,6 +4470,9 @@ bool ScFormulaCell::InterpretFormulaGroup()
             return false;
     }
 
+    if (!bCanVectorize)
+        return false;
+
     if (!ScCalcConfig::isOpenCLEnabled() && !ScCalcConfig::isSwInterpreterEnabled())
     {
         aScope.addMessage("opencl not enabled and sw interpreter not enabled");
diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index f637ab5753bc..e22e294b9262 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -18,9 +18,11 @@
 #include <interpre.hxx>
 #include <scmatrix.hxx>
 #include <globalnames.hxx>
+#include <comphelper/threadpool.hxx>
 
 #include <formula/vectortoken.hxx>
 #include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Calc.hxx>
 #if HAVE_FEATURE_OPENCL
 #include <opencl/platforminfo.hxx>
 #endif
@@ -145,117 +147,208 @@ ScMatrixRef FormulaGroupInterpreterSoftware::inverseMatrix(const ScMatrix& /*rMa
     return ScMatrixRef();
 }
 
+class SoftwareInterpreterFunc
+{
+public:
+    SoftwareInterpreterFunc(ScTokenArray& rCode,
+                            ScAddress aBatchTopPos,
+                            const ScAddress& rTopPos,
+                            ScDocument& rDoc,
+                            std::vector<formula::FormulaConstTokenRef>& rRes,
+                            SCROW nIndex,
+                            SCROW nLastIndex) :
+        mrCode(rCode),
+        maBatchTopPos(aBatchTopPos),
+        mrTopPos(rTopPos),
+        mrDoc(rDoc),
+        mrResults(rRes),
+        mnIdx(nIndex),
+        mnLastIdx(nLastIndex)
+    {
+    }
+
+    void operator() ()
+    {
+        double fNan;
+        rtl::math::setNan(&fNan);
+        for (SCROW i = mnIdx; i <= mnLastIdx; ++i, maBatchTopPos.IncRow())
+        {
+            ScTokenArray aCode2;
+            formula::FormulaTokenArrayPlainIterator aIter(mrCode);
+            for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+            {
+                switch (p->GetType())
+                {
+                    case formula::svSingleVectorRef:
+                    {
+                        const formula::SingleVectorRefToken* p2 = static_cast<const formula::SingleVectorRefToken*>(p);
+                        const formula::VectorRefArray& rArray = p2->GetArray();
+
+                        rtl_uString* pStr = nullptr;
+                        double fVal = fNan;
+                        if (static_cast<size_t>(i) < p2->GetArrayLength())
+                        {
+                            if (rArray.mpStringArray)
+                                // See if the cell is of string type.
+                                pStr = rArray.mpStringArray[i];
+
+                            if (!pStr && rArray.mpNumericArray)
+                                fVal = rArray.mpNumericArray[i];
+                        }
+
+                        if (pStr)
+                        {
+                            // This is a string cell.
+                            svl::SharedStringPool& rPool = mrDoc.GetSharedStringPool();
+                            aCode2.AddString(rPool.intern(OUString(pStr)));
+                        }
+                        else if (rtl::math::isNan(fVal))
+                            // Value of NaN represents an empty cell.
+                            aCode2.AddToken(ScEmptyCellToken(false, false));
+                        else
+                            // Numeric cell.
+                            aCode2.AddDouble(fVal);
+                    }
+                    break;
+                    case formula::svDoubleVectorRef:
+                    {
+                        const formula::DoubleVectorRefToken* p2 = static_cast<const formula::DoubleVectorRefToken*>(p);
+                        size_t nRowStart = p2->IsStartFixed() ? 0 : i;
+                        size_t nRowEnd = p2->GetRefRowSize() - 1;
+                        if (!p2->IsEndFixed())
+                            nRowEnd += i;
+
+                        assert(nRowStart <= nRowEnd);
+                        ScMatrixRef pMat(new ScVectorRefMatrix(p2, nRowStart, nRowEnd - nRowStart + 1));
+
+                        if (p2->IsStartFixed() && p2->IsEndFixed())
+                        {
+                            // Cached the converted token for absolute range reference.
+                            ScComplexRefData aRef;
+                            ScRange aRefRange = mrTopPos;
+                            aRefRange.aEnd.SetRow(mrTopPos.Row() + nRowEnd);
+                            aRef.InitRange(aRefRange);
+                            formula::FormulaTokenRef xTok(new ScMatrixRangeToken(pMat, aRef));
+                            aCode2.AddToken(*xTok);
+                        }
+                        else
+                        {
+                            ScMatrixToken aTok(pMat);
+                            aCode2.AddToken(aTok);
+                        }
+                    }
+                    break;
+                    default:
+                        aCode2.AddToken(*p);
+                } // end of switch statement
+            } // end of formula token for loop
+
+            ScFormulaCell* pDest = mrDoc.GetFormulaCell(maBatchTopPos);
+            if (!pDest)
+                return;
+
+            ScCompiler aComp(&mrDoc, maBatchTopPos, aCode2);
+            aComp.CompileTokenArray();
+            ScInterpreter aInterpreter(pDest, &mrDoc, mrDoc.GetNonThreadedContext(), maBatchTopPos, aCode2);
+            aInterpreter.Interpret();
+            mrResults[i] = aInterpreter.GetResultToken();
+        } // Row iteration for loop end
+    } // operator () end
+
+private:
+    ScTokenArray& mrCode;
+    ScAddress maBatchTopPos;
+    const ScAddress& mrTopPos;
+    ScDocument& mrDoc;
+    std::vector<formula::FormulaConstTokenRef>& mrResults;
+    SCROW mnIdx;
+    SCROW mnLastIdx;
+};
+
 bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddress& rTopPos,
                                                 ScFormulaCellGroupRef& xGroup,
                                                 ScTokenArray& rCode)
 {
-    typedef std::unordered_map<const formula::FormulaToken*, formula::FormulaTokenRef> CachedTokensType;
-
     // Decompose the group into individual cells and calculate them individually.
 
     // The caller must ensure that the top position is the start position of
     // the group.
 
     ScAddress aTmpPos = rTopPos;
-    std::vector<formula::FormulaConstTokenRef> aResults;
-    aResults.reserve(xGroup->mnLength);
-    CachedTokensType aCachedTokens;
-
-    double fNan;
-    rtl::math::setNan(&fNan);
+    std::vector<formula::FormulaConstTokenRef> aResults(xGroup->mnLength);
 
-    for (SCROW i = 0; i < xGroup->mnLength; ++i, aTmpPos.IncRow())
+    class Executor : public comphelper::ThreadTask
     {
-        ScTokenArray aCode2;
-        formula::FormulaTokenArrayPlainIterator aIter(rCode);
-        for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+    public:
+        Executor(std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
+                 ScTokenArray& rCode2,
+                 ScAddress aBatchTopPos,
+                 const ScAddress& rTopPos2,
+                 ScDocument& rDoc2,
+                 std::vector<formula::FormulaConstTokenRef>& rRes,
+                 SCROW nIndex,
+                 SCROW nLastIndex) :
+            comphelper::ThreadTask(rTag),
+            maSWIFunc(rCode2, aBatchTopPos, rTopPos2, rDoc2, rRes, nIndex, nLastIndex)
         {
-            CachedTokensType::iterator it = aCachedTokens.find(p);
-            if (it != aCachedTokens.end())
-            {
-                // This token is cached. Use the cached one.
-                aCode2.AddToken(*it->second);
-                continue;
-            }
+        }
+        virtual void doWork() override
+        {
+            maSWIFunc();
+        }
 
-            switch (p->GetType())
-            {
-                case formula::svSingleVectorRef:
-                {
-                    const formula::SingleVectorRefToken* p2 = static_cast<const formula::SingleVectorRefToken*>(p);
-                    const formula::VectorRefArray& rArray = p2->GetArray();
+    private:
+        SoftwareInterpreterFunc maSWIFunc;
+    };
 
-                    rtl_uString* pStr = nullptr;
-                    double fVal = fNan;
-                    if (static_cast<size_t>(i) < p2->GetArrayLength())
-                    {
-                        if (rArray.mpStringArray)
-                            // See if the cell is of string type.
-                            pStr = rArray.mpStringArray[i];
+    static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
 
-                        if (!pStr && rArray.mpNumericArray)
-                            fVal = rArray.mpNumericArray[i];
-                    }
+    bool bUseThreading = !bThreadingProhibited && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get();
 
-                    if (pStr)
-                    {
-                        // This is a string cell.
-                        svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
-                        aCode2.AddString(rPool.intern(OUString(pStr)));
-                    }
-                    else if (rtl::math::isNan(fVal))
-                        // Value of NaN represents an empty cell.
-                        aCode2.AddToken(ScEmptyCellToken(false, false));
-                    else
-                        // Numeric cell.
-                        aCode2.AddDouble(fVal);
-                }
-                break;
-                case formula::svDoubleVectorRef:
-                {
-                    const formula::DoubleVectorRefToken* p2 = static_cast<const formula::DoubleVectorRefToken*>(p);
-                    size_t nRowStart = p2->IsStartFixed() ? 0 : i;
-                    size_t nRowEnd = p2->GetRefRowSize() - 1;
-                    if (!p2->IsEndFixed())
-                        nRowEnd += i;
+    if (bUseThreading)
+    {
+        comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
+        sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
+
+        SCROW nLen = xGroup->mnLength;
+        SCROW nBatchSize = nLen / nThreadCount;
+        if (nLen < nThreadCount)
+        {
+            nBatchSize = 1;
+            nThreadCount = nLen;
+        }
+        SCROW nRemaining = nLen - nBatchSize * nThreadCount;
 
-                    assert(nRowStart <= nRowEnd);
-                    ScMatrixRef pMat(new ScVectorRefMatrix(p2, nRowStart, nRowEnd - nRowStart + 1));
+        SAL_INFO("sc.swipret.threaded", "Running " << nThreadCount << " threads");
 
-                    if (p2->IsStartFixed() && p2->IsEndFixed())
-                    {
-                        // Cached the converted token for absolute range reference.
-                        ScComplexRefData aRef;
-                        ScRange aRefRange = rTopPos;
-                        aRefRange.aEnd.SetRow(rTopPos.Row() + nRowEnd);
-                        aRef.InitRange(aRefRange);
-                        formula::FormulaTokenRef xTok(new ScMatrixRangeToken(pMat, aRef));
-                        aCachedTokens.emplace(p, xTok);
-                        aCode2.AddToken(*xTok);
-                    }
-                    else
-                    {
-                        ScMatrixToken aTok(pMat);
-                        aCode2.AddToken(aTok);
-                    }
-                }
-                break;
-                default:
-                    aCode2.AddToken(*p);
-            }
+        SCROW nLeft = nLen;
+        SCROW nStart = 0;
+        std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
+        while (nLeft > 0)
+        {
+            SCROW nCount = std::min(nLeft, nBatchSize) + (nRemaining ? 1 : 0);
+            if ( nRemaining )
+                --nRemaining;
+            SCROW nLast = nStart + nCount - 1;
+            rThreadPool.pushTask(new Executor(aTag, rCode, aTmpPos, rTopPos, rDoc, aResults, nStart, nLast));
+            aTmpPos.IncRow(nCount);
+            nLeft -= nCount;
+            nStart = nLast + 1;
         }
+        SAL_INFO("sc.swipret.threaded", "Joining threads");
+        rThreadPool.waitUntilDone(aTag);
+        SAL_INFO("sc.swipret.threaded", "Done");
+    }
+    else
+    {
+        SoftwareInterpreterFunc aSWIFunc(rCode, aTmpPos, rTopPos, rDoc, aResults, 0, xGroup->mnLength - 1);
+        aSWIFunc();
+    }
 
-        ScFormulaCell* pDest = rDoc.GetFormulaCell(aTmpPos);
-        if (!pDest)
+    for (SCROW i = 0; i < xGroup->mnLength; ++i)
+        if (!aResults[i].get())
             return false;
 
-        ScCompiler aComp(&rDoc, aTmpPos, aCode2);
-        aComp.CompileTokenArray();
-        ScInterpreter aInterpreter(pDest, &rDoc, rDoc.GetNonThreadedContext(), aTmpPos, aCode2);
-        aInterpreter.Interpret();
-        aResults.push_back(aInterpreter.GetResultToken());
-    } // for loop end (xGroup->mnLength)
-
     if (!aResults.empty())
         rDoc.SetFormulaResults(rTopPos, &aResults[0], aResults.size());
 
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 43f0825b3f1e..e8e30b9d24ad 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -1336,7 +1336,7 @@ bool ScTokenArray::AddFormulaToken(
     return bError;
 }
 
-void ScTokenArray::CheckToken( const FormulaToken& r )
+void ScTokenArray::CheckForThreading( OpCode eOp  )
 {
     static const std::set<OpCode> aThreadedCalcBlackList({
         ocIndirect,
@@ -1345,23 +1345,32 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
         ocTableOp
     });
 
-    if (IsFormulaVectorDisabled())
-        // It's already disabled.  No more checking needed.
-        return;
+    // We only call this if it was already disabled
+    assert(IsFormulaVectorDisabled());
 
     static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
 
-    OpCode eOp = r.GetOpCode();
-
     if (!bThreadingProhibited && !ScCalcConfig::isOpenCLEnabled() && officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
     {
         if (aThreadedCalcBlackList.count(eOp))
         {
-            meVectorState = FormulaVectorDisabledNotInSubSet;
             SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables threaded calculation of formula group");
         }
-        return;
+        else
+        {
+            SAL_INFO("sc.core.formulagroup", "but enabling for threading instead");
+            meVectorState = FormulaVectorEnabledForThreading;
+        }
     }
+}
+
+void ScTokenArray::CheckToken( const FormulaToken& r )
+{
+    if (IsFormulaVectorDisabled())
+        // It's already disabled.  No more checking needed.
+        return;
+
+    OpCode eOp = r.GetOpCode();
 
     if (SC_OPCODE_START_FUNCTION <= eOp && eOp < SC_OPCODE_STOP_FUNCTION)
     {
@@ -1370,6 +1379,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
         {
             SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables vectorisation for formula group");
             meVectorState = FormulaVectorDisabledNotInSubSet;
+            CheckForThreading(eOp);
             return;
         }
 
@@ -1381,6 +1391,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
         {
             SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables S/W interpreter for formula group");
             meVectorState = FormulaVectorDisabledNotInSoftwareSubset;
+            CheckForThreading(eOp);
             return;
         }
 
@@ -1608,6 +1619,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
                 // We don't support vectorization on these.
                 SAL_INFO("sc.opencl", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType()) << " disables vectorisation for formula group");
                 meVectorState = FormulaVectorDisabledByStackVariable;
+                CheckForThreading(eOp);
             break;
             default:
                 ;
@@ -1619,6 +1631,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
     {
         SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables vectorisation for formula group");
         meVectorState = FormulaVectorDisabledNotInSubSet;
+        CheckForThreading(eOp);
     }
     // only when openCL interpreter is not enabled - the assumption is that
     // the S/W interpreter blacklist is more strict
@@ -1629,6 +1642,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
     {
         SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp) << " disables S/W interpreter for formula group");
         meVectorState = FormulaVectorDisabledNotInSoftwareSubset;
+        CheckForThreading(eOp);
     }
 }
 
@@ -1756,6 +1770,7 @@ bool ScTokenArray::IsFormulaVectorDisabled() const
         case FormulaVectorDisabledNotInSoftwareSubset:
         case FormulaVectorDisabledByStackVariable:
         case FormulaVectorDisabledNotInSubSet:
+        case FormulaVectorEnabledForThreading:
             return true;
         default:
             ;


More information about the Libreoffice-commits mailing list