Find actual data area inside the main-range...
and trim all ranges to match this actual data area. Don't do
this optimization for COUNTIFS where there is no main-range.
This optimization is also turned off if any of the parameter
ranges are not double-refs.
Benefits in cases like -
=SUMIFS(A:A, B:B, ">=20", C:C, "<=10") and the is data only
in say A1:A10000 and rest are empty. Range trimming in this
case saves lot of execution time and memory (for vConditions
and vRefArrayConditions).
Change-Id: I6b4ad91e23f89dc48603b98000bcef4dbdb03112
Reviewed-on: https://gerrit.libreoffice.org/64657
Tested-by: Jenkins
Reviewed-by: Eike Rathke <erack@redhat.com>
(cherry picked from commit cb52b112c0d44d7a6849f773fd1ee4e863ab91da)
Reviewed-on: https://gerrit.libreoffice.org/64833
Reviewed-by: Dennis Francis <dennis.francis@collabora.com>
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 0a328f0..b1e890a6 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -222,6 +222,7 @@ public:
bool bConsiderCellDrawObjects=false ) const;
bool GetPrevDataPos(SCROW& rRow) const;
bool GetNextDataPos(SCROW& rRow) const;
bool TrimEmptyBlocks(SCROW& rRowStart, SCROW& rRowEnd) const;
void FindDataAreaPos(SCROW& rRow, bool bDown) const; // (without Broadcaster)
void FindUsed( SCROW nStartRow, SCROW nEndRow, mdds::flat_segment_tree<SCROW, bool>& rUsed ) const;
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 04ff9a0..31278f8 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1385,6 +1385,15 @@ public:
SCCOL& rEndCol, SCROW& rEndRow,
bool bIncludeOld, bool bOnlyDown ) const;
/**
* Returns true if there is a non-empty subrange in the range given as input.
* In that case it also modifies rRange to largest subrange that does not
* have empty col/row inrange-segments in the beginning/end.
* It returns false if rRange is completely empty and in this case rRange is
* left unmodified.
*/
bool GetDataAreaSubrange(ScRange& rRange) const;
SC_DLLPUBLIC bool GetCellArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const;
SC_DLLPUBLIC bool GetTableArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const;
SC_DLLPUBLIC bool GetPrintArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow,
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index aae436e..e804a1c 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -576,6 +576,8 @@ public:
void GetDataArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow,
bool bIncludeOld, bool bOnlyDown ) const;
bool GetDataAreaSubrange( ScRange& rRange ) const;
bool ShrinkToUsedDataArea( bool& o_bShrunk, SCCOL& rStartCol, SCROW& rStartRow,
SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly,
bool bStickyTopRow, bool bStickyLeftCol, bool bConsiderCellNotes,
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index d2e81de..c46f1a70 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -1405,6 +1405,52 @@ bool ScColumn::GetNextDataPos(SCROW& rRow) const // greater than rRow
return true;
}
bool ScColumn::TrimEmptyBlocks(SCROW& rRowStart, SCROW& rRowEnd) const
{
assert(rRowStart <= rRowEnd);
SCROW nRowStartNew = rRowStart, nRowEndNew = rRowEnd;
// Trim down rRowStart first
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRowStart);
sc::CellStoreType::const_iterator it = aPos.first;
if (it == maCells.end())
return false;
if (it->type == sc::element_type_empty)
{
// This block is empty. Skip ahead to the next block (if exists).
nRowStartNew += it->size - aPos.second;
if (nRowStartNew > rRowEnd)
return false;
++it;
if (it == maCells.end())
// No more next block.
return false;
}
// Trim up rRowEnd next
aPos = maCells.position(rRowEnd);
it = aPos.first;
if (it == maCells.end())
{
rRowStart = nRowStartNew;
return true; // Because trimming of rRowStart is ok
}
if (it->type == sc::element_type_empty)
{
// rRowEnd cannot be in the first block which is empty !
assert(it != maCells.begin());
// This block is empty. Skip to the previous block (it exists).
nRowEndNew -= aPos.second + 1; // Last row position of the previous block.
assert(nRowStartNew <= nRowEndNew);
}
rRowStart = nRowStartNew;
rRowEnd = nRowEndNew;
return true;
}
SCROW ScColumn::FindNextVisibleRow(SCROW nRow, bool bForward) const
{
if(bForward)
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 9693edf..dbba8a2 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -1098,6 +1098,18 @@ void ScDocument::GetDataArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
maTabs[nTab]->GetDataArea( rStartCol, rStartRow, rEndCol, rEndRow, bIncludeOld, bOnlyDown );
}
bool ScDocument::GetDataAreaSubrange(ScRange& rRange) const
{
SCTAB nTab = rRange.aStart.Tab();
if (nTab != rRange.aEnd.Tab())
return true;
if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
return maTabs[nTab]->GetDataAreaSubrange(rRange);
return true;
}
void ScDocument::LimitChartArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
SCCOL& rEndCol, SCROW& rEndRow )
{
diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx
index ede4096..cef0563 100644
--- a/sc/source/core/data/table1.cxx
+++ b/sc/source/core/data/table1.cxx
@@ -904,6 +904,47 @@ void ScTable::GetDataArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, S
}
}
bool ScTable::GetDataAreaSubrange( ScRange& rRange ) const
{
SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
if ( nCol1 >= aCol.size() )
return false;
nCol2 = std::min<SCCOL>( nCol2, aCol.size()-1 );
SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
SCCOL nFirstNonEmptyCol = -1, nLastNonEmptyCol = -1;
SCROW nRowStart = nRow2, nRowEnd = nRow1;
for ( SCCOL nCol = nCol1; nCol <= nCol2; ++nCol )
{
SCROW nRowStartThis = nRow1, nRowEndThis = nRow2;
bool bTrimmed = aCol[nCol].TrimEmptyBlocks(nRowStartThis, nRowEndThis);
if ( bTrimmed )
{
if ( nFirstNonEmptyCol == -1 )
nFirstNonEmptyCol = nCol;
nLastNonEmptyCol = nCol;
nRowStart = std::min<SCROW>(nRowStart, nRowStartThis);
nRowEnd = std::max<SCROW>(nRowEnd, nRowEndThis);
}
}
if ( nFirstNonEmptyCol == -1 )
return false;
assert(nFirstNonEmptyCol <= nLastNonEmptyCol);
assert(nRowStart <= nRowEnd);
rRange.aStart.Set(nFirstNonEmptyCol, nRowStart, rRange.aStart.Tab());
rRange.aEnd.Set(nLastNonEmptyCol, nRowEnd, rRange.aEnd.Tab());
return true;
}
bool ScTable::ShrinkToUsedDataArea( bool& o_bShrunk, SCCOL& rStartCol, SCROW& rStartRow,
SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly, bool bStickyTopRow, bool bStickyLeftCol,
bool bConsiderCellNotes, bool bConsiderCellDrawObjects ) const
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index 9476c64..d7279c1 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -5842,6 +5842,55 @@ void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIf
// with a single InterpretTail() call it results in evaluation of all the cells in the
// matrix formula.
vConditions.clear();
SCCOL nStartColDiff = 0;
SCCOL nEndColDiff = 0;
SCROW nStartRowDiff = 0;
SCROW nEndRowDiff = 0;
bool bRangeReduce = false;
// Range-reduce optimization
if (nParamCount % 2) // Not COUNTIFS
{
bool bHasDoubleRefCriteriaRanges = true;
// Do not attempt main-range reduce if any of the criteria-ranges are not double-refs.
for (sal_uInt16 nParamIdx = 2; nParamIdx < nParamCount; nParamIdx += 2 )
{
const formula::FormulaToken* pCriteriaRangeToken = pStack[ sp-nParamIdx ];
if (pCriteriaRangeToken->GetType() != svDoubleRef )
{
bHasDoubleRefCriteriaRanges = false;
break;
}
}
// Probe the main range token, and try if we can shrink the range without altering results.
const formula::FormulaToken* pMainRangeToken = pStack[ sp-nParamCount ];
if (pMainRangeToken->GetType() == svDoubleRef && bHasDoubleRefCriteriaRanges)
{
const ScComplexRefData* pRefData = pMainRangeToken->GetDoubleRef();
if (!pRefData->IsDeleted())
{
ScRange aMainRange, aSubRange;
DoubleRefToRange( *pRefData, aMainRange);
if (aMainRange.aStart.Tab() == aMainRange.aEnd.Tab())
{
// Shrink the range to actual data content.
aSubRange = aMainRange;
pDok->GetDataAreaSubrange(aSubRange);
nStartColDiff = aSubRange.aStart.Col() - aMainRange.aStart.Col();
nStartRowDiff = aSubRange.aStart.Row() - aMainRange.aStart.Row();
nEndColDiff = aSubRange.aEnd.Col() - aMainRange.aEnd.Col();
nEndRowDiff = aSubRange.aEnd.Row() - aMainRange.aEnd.Row();
bRangeReduce = nStartColDiff || nStartRowDiff || nEndColDiff || nEndRowDiff;
}
}
}
}
double fVal = 0.0;
SCCOL nDimensionCols = 0;
SCROW nDimensionRows = 0;
@@ -6024,6 +6073,15 @@ void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIf
return;
}
if (bRangeReduce)
{
nCol1 += nStartColDiff;
nRow1 += nStartRowDiff;
nCol2 += nEndColDiff;
nRow2 += nEndRowDiff;
}
// All reference ranges must be of same dimension and size.
if (!nDimensionCols)
nDimensionCols = nCol2 - nCol1 + 1;
@@ -6262,6 +6320,15 @@ void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIf
return;
}
if (bRangeReduce)
{
nMainCol1 += nStartColDiff;
nMainRow1 += nStartRowDiff;
nMainCol2 += nEndColDiff;
nMainRow2 += nEndRowDiff;
}
// All reference ranges must be of same dimension and size.
if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1)))
{