sc/source/core/tool/compiler.cxx |  225 +++++++++++++++++++++++++++------------
 1 file changed, 157 insertions(+), 68 deletions(-)

New commits:
commit 6bd6f01b31bfd0175670ed3848d4ae0ff9138194
Author:     Noel Grandin <noel.gran...@collabora.co.uk>
AuthorDate: Wed Dec 28 17:30:12 2022 +0200
Commit:     Dennis Francis <dennis.fran...@collabora.com>
CommitDate: Mon Jan 2 04:32:29 2023 +0000

    optimise SUMPRODUCT(IF..) where we have entire column/row ranges
    
    following the same pattern as for SUM, set those ranges to shrink to
    available data.
    
    Takes it from 3s to <500ms on my test spreadsheet.
    
    Change-Id: I53ada996393a063b12ef7dd0f9bff40c90ecc8be
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144850
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>
    (cherry picked from commit 9713a4aae980a52d08d38f5b12424aa5e33f9a75)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144901
    Reviewed-by: Michael Meeks <michael.me...@collabora.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Dennis Francis <dennis.fran...@collabora.com>

diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index f1535a00fb9b..81c45db54b92 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -6452,89 +6452,178 @@ void ScCompiler::AnnotateTrimOnDoubleRefs()
     // OpCode of the "root" operator (which is already in RPN array).
     OpCode eOpCode = (*(pCode - 1))->GetOpCode();
     // eOpCode can be some operator which does not change with operands with 
or contains zero values.
-    if (eOpCode != ocSum)
-        return;
+    if (eOpCode == ocSum)
+    {
+        FormulaToken** ppTok = pCode - 2; // exclude the root operator.
+        // The following loop runs till a "pattern" is found or there is a 
mismatch
+        // and marks the push DoubleRef arguments as trimmable when there is a 
match.
+        // The pattern is
+        // SUM(IF(<reference|double>=<reference|double>, <then-clause>)<a some 
operands with operators / or *>)
+        // such that one of the operands of ocEqual is a double-ref.
+        // Examples of formula that matches this are:
+        //   SUM(IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)
+        //   SUM((IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)*$H$2*5/$G$3)
+        //   SUM(IF(E:E=16,F:F)*$H$1*100)
+        bool bTillClose = true;
+        bool bCloseTillIf = false;
+        sal_Int16 nToksTillIf = 0;
+        constexpr sal_Int16 MAXDIST_IF = 15;
+        while (*ppTok)
+        {
+            FormulaToken* pTok = *ppTok;
+            OpCode eCurrOp = pTok->GetOpCode();
+            ++nToksTillIf;
+
+            // TODO : Is there a better way to handle this ?
+            // ocIf is too far off from the sum opcode.
+            if (nToksTillIf > MAXDIST_IF)
+                return;
 
-    FormulaToken** ppTok = pCode - 2; // exclude the root operator.
-    // The following loop runs till a "pattern" is found or there is a mismatch
-    // and marks the push DoubleRef arguments as trimmable when there is a 
match.
-    // The pattern is
-    // SUM(IF(<reference|double>=<reference|double>, <then-clause>)<a some 
operands with operators / or *>)
-    // such that one of the operands of ocEqual is a double-ref.
-    // Examples of formula that matches this are:
-    //   SUM(IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)
-    //   SUM((IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)*$H$2*5/$G$3)
-    //   SUM(IF(E:E=16,F:F)*$H$1*100)
-    bool bTillClose = true;
-    bool bCloseTillIf = false;
-    sal_Int16 nToksTillIf = 0;
-    constexpr sal_Int16 MAXDIST_IF = 15;
-    while (*ppTok)
-    {
-        FormulaToken* pTok = *ppTok;
-        OpCode eCurrOp = pTok->GetOpCode();
-        ++nToksTillIf;
-
-        // TODO : Is there a better way to handle this ?
-        // ocIf is too far off from the sum opcode.
-        if (nToksTillIf > MAXDIST_IF)
-            return;
+            switch (eCurrOp)
+            {
+                case ocDiv:
+                case ocMul:
+                    if (!bTillClose)
+                        return;
+                    break;
+                case ocPush:
 
-        switch (eCurrOp)
-        {
-            case ocDiv:
-            case ocMul:
-                if (!bTillClose)
-                    return;
-                break;
-            case ocPush:
+                    break;
+                case ocClose:
+                    if (bTillClose)
+                    {
+                        bTillClose = false;
+                        bCloseTillIf = true;
+                    }
+                    else
+                        return;
+                    break;
+                case ocIf:
+                    {
+                        if (!bCloseTillIf)
+                            return;
 
-                break;
-            case ocClose:
-                if (bTillClose)
-                {
-                    bTillClose = false;
-                    bCloseTillIf = true;
-                }
-                else
+                        if (!pTok->IsInForceArray())
+                            return;
+
+                        const short nJumpCount = pTok->GetJump()[0];
+                        if (nJumpCount != 2) // Should have THEN but no ELSE.
+                            return;
+
+                        OpCode eCompOp = (*(ppTok - 1))->GetOpCode();
+                        if (eCompOp != ocEqual)
+                            return;
+
+                        FormulaToken* pLHS = *(ppTok - 2);
+                        FormulaToken* pRHS = *(ppTok - 3);
+                        if (((pLHS->GetType() == svSingleRef || 
pLHS->GetType() == svDouble) && pRHS->GetType() == svDoubleRef) ||
+                            ((pRHS->GetType() == svSingleRef || 
pRHS->GetType() == svDouble) && pLHS->GetType() == svDoubleRef))
+                        {
+                            if (pLHS->GetType() == svDoubleRef)
+                                pLHS->GetDoubleRef()->SetTrimToData(true);
+                            else
+                                pRHS->GetDoubleRef()->SetTrimToData(true);
+                            return;
+                        }
+                    }
+                    break;
+                default:
                     return;
-                break;
-            case ocIf:
-                {
-                    if (!bCloseTillIf)
-                        return;
+            }
+            --ppTok;
+        }
+    }
+    else if (eOpCode == ocSumProduct)
+    {
+        FormulaToken** ppTok = pCode - 2; // exclude the root operator.
+        // The following loop runs till a "pattern" is found or there is a 
mismatch
+        // and marks the push DoubleRef arguments as trimmable when there is a 
match.
+        // The pattern is
+        // SUMPRODUCT(IF(<reference|double>=<reference|double>, 
<then-clause>)<a some operands with operators / or *>)
+        // such that one of the operands of ocEqual is a double-ref.
+        // Examples of formula that matches this are:
+        //   SUMPRODUCT(IF($A:$A=$L12;$D:$D*G:G))
+        bool bTillClose = true;
+        bool bCloseTillIf = false;
+        sal_Int16 nToksTillIf = 0;
+        constexpr sal_Int16 MAXDIST_IF = 15;
+        while (*ppTok)
+        {
+            FormulaToken* pTok = *ppTok;
+            OpCode eCurrOp = pTok->GetOpCode();
+            ++nToksTillIf;
+
+            // TODO : Is there a better way to handle this ?
+            // ocIf is too far off from the sum opcode.
+            if (nToksTillIf > MAXDIST_IF)
+                return;
 
-                    if (!pTok->IsInForceArray())
+            switch (eCurrOp)
+            {
+                case ocDiv:
+                case ocMul:
+                    {
+                        if (!pTok->IsInForceArray())
+                            break;
+                        FormulaToken* pLHS = *(ppTok - 1);
+                        FormulaToken* pRHS = *(ppTok - 2);
+                        StackVar lhsType = pLHS->GetType();
+                        StackVar rhsType = pRHS->GetType();
+                        if (lhsType == svDoubleRef && rhsType == svDoubleRef)
+                        {
+                            pLHS->GetDoubleRef()->SetTrimToData(true);
+                            pRHS->GetDoubleRef()->SetTrimToData(true);
+                        }
+                    }
+                    break;
+                case ocPush:
+                    break;
+                case ocClose:
+                    if (bTillClose)
+                    {
+                        bTillClose = false;
+                        bCloseTillIf = true;
+                    }
+                    else
                         return;
+                    break;
+                case ocIf:
+                    {
+                        if (!bCloseTillIf)
+                            return;
 
-                    const short nJumpCount = pTok->GetJump()[0];
-                    if (nJumpCount != 2) // Should have THEN but no ELSE.
-                        return;
+                        if (!pTok->IsInForceArray())
+                            return;
 
-                    OpCode eCompOp = (*(ppTok - 1))->GetOpCode();
-                    if (eCompOp != ocEqual)
-                        return;
+                        const short nJumpCount = pTok->GetJump()[0];
+                        if (nJumpCount != 2) // Should have THEN but no ELSE.
+                            return;
 
-                    FormulaToken* pLHS = *(ppTok - 2);
-                    FormulaToken* pRHS = *(ppTok - 3);
-                    if (((pLHS->GetType() == svSingleRef || pLHS->GetType() == 
svDouble) && pRHS->GetType() == svDoubleRef) ||
-                        ((pRHS->GetType() == svSingleRef || pRHS->GetType() == 
svDouble) && pLHS->GetType() == svDoubleRef))
-                    {
-                        if (pLHS->GetType() == svDoubleRef)
+                        OpCode eCompOp = (*(ppTok - 1))->GetOpCode();
+                        if (eCompOp != ocEqual)
+                            return;
+
+                        FormulaToken* pLHS = *(ppTok - 2);
+                        FormulaToken* pRHS = *(ppTok - 3);
+                        StackVar lhsType = pLHS->GetType();
+                        StackVar rhsType = pRHS->GetType();
+                        if (lhsType == svDoubleRef && (rhsType == svSingleRef 
|| rhsType == svDouble))
+                        {
                             pLHS->GetDoubleRef()->SetTrimToData(true);
-                        else
+                        }
+                        if ((lhsType == svSingleRef || lhsType == svDouble) && 
rhsType == svDoubleRef)
+                        {
                             pRHS->GetDoubleRef()->SetTrimToData(true);
+                        }
                         return;
                     }
-                }
-                break;
-            default:
-                return;
+                    break;
+                default:
+                    return;
+            }
+            --ppTok;
         }
-        --ppTok;
     }
-
-    return;
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to