tdf#128812 speed up loading calc doc with lots of countif
by creating a copy of ScQueryCellIterator that is specialised for this
use-case.
Takes the opening time from 50s to 8s on my machine.
Change-Id: I193a7c181a5dfed6fecf75e871729d73625d0df6
Reviewed-on: https://gerrit.libreoffice.org/83299
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 18cf3de..74fa4e8 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -141,6 +141,7 @@ friend class ScValueIterator;
friend class ScHorizontalValueIterator;
friend class ScDBQueryDataIterator;
friend class ScQueryCellIterator;
friend class ScCountIfCellIterator;
friend class ScFormulaGroupIterator;
friend class ScCellIterator;
friend class ScHorizontalCellIterator;
diff --git a/sc/inc/dociter.hxx b/sc/inc/dociter.hxx
index a6a8d37..3479ee6 100644
--- a/sc/inc/dociter.hxx
+++ b/sc/inc/dociter.hxx
@@ -367,6 +367,31 @@ public:
bool FindEqualOrSortedLastInRange( SCCOL& nFoundCol, SCROW& nFoundRow );
};
// Used by ScInterpreter::ScCountIf.
// Walk through all non-empty cells in an area.
class ScCountIfCellIterator
{
typedef sc::CellStoreType::const_position_type PositionType;
PositionType maCurPos;
ScQueryParam maParam;
ScDocument* pDoc;
const ScInterpreterContext& mrContext;
SCTAB nTab;
SCCOL nCol;
SCROW nRow;
/** Initialize position for new column. */
void InitPos();
void IncPos();
void IncBlock();
void AdvanceQueryParamEntryField();
public:
ScCountIfCellIterator(ScDocument* pDocument, const ScInterpreterContext& rContext, SCTAB nTable,
const ScQueryParam& aParam);
int GetCount();
};
class ScDocAttrIterator // all attribute areas
{
private:
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 66ed8fe..d774cdb 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -316,6 +316,7 @@ friend class ScDBQueryDataIterator;
friend class ScFormulaGroupIterator;
friend class ScCellIterator;
friend class ScQueryCellIterator;
friend class ScCountIfCellIterator;
friend class ScHorizontalCellIterator;
friend class ScHorizontalAttrIterator;
friend class ScDocAttrIterator;
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index a31e619..49ff6b2 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -250,6 +250,7 @@ friend class ScDBQueryDataIterator;
friend class ScFormulaGroupIterator;
friend class ScCellIterator;
friend class ScQueryCellIterator;
friend class ScCountIfCellIterator;
friend class ScHorizontalCellIterator;
friend class ScHorizontalAttrIterator;
friend class ScDocAttrIterator;
diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx
index 3c8e369..919c41c7 100644
--- a/sc/source/core/data/dociter.cxx
+++ b/sc/source/core/data/dociter.cxx
@@ -1449,6 +1449,146 @@ bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol,
return (nFoundCol <= pDoc->MaxCol()) && (nFoundRow <= pDoc->MaxRow());
}
ScCountIfCellIterator::ScCountIfCellIterator(ScDocument* pDocument, const ScInterpreterContext& rContext, SCTAB nTable,
const ScQueryParam& rParam ) :
maParam(rParam),
pDoc( pDocument ),
mrContext( rContext ),
nTab( nTable)
{
nCol = maParam.nCol1;
nRow = maParam.nRow1;
}
void ScCountIfCellIterator::InitPos()
{
nRow = maParam.nRow1;
if (maParam.bHasHeader && maParam.bByRow)
++nRow;
ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol];
maCurPos = pCol->maCells.position(nRow);
}
void ScCountIfCellIterator::IncPos()
{
if (maCurPos.second + 1 < maCurPos.first->size)
{
// Move within the same block.
++maCurPos.second;
++nRow;
}
else
// Move to the next block.
IncBlock();
}
void ScCountIfCellIterator::IncBlock()
{
++maCurPos.first;
maCurPos.second = 0;
nRow = maCurPos.first->position;
}
int ScCountIfCellIterator::GetCount()
{
assert(nTab < pDoc->GetTableCount() && "try to access index out of bounds, FIX IT");
nCol = maParam.nCol1;
InitPos();
const ScQueryEntry& rEntry = maParam.GetEntry(0);
const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
const bool bSingleQueryItem = rEntry.GetQueryItems().size() == 1;
int count = 0;
ScColumn* pCol = &(pDoc->maTabs[nTab])->aCol[nCol];
while (true)
{
bool bNextColumn = maCurPos.first == pCol->maCells.end();
if (!bNextColumn)
{
if (nRow > maParam.nRow2)
bNextColumn = true;
}
if (bNextColumn)
{
do
{
++nCol;
if (nCol > maParam.nCol2 || nCol >= pDoc->maTabs[nTab]->GetAllocatedColumnsCount())
return count; // Over and out
AdvanceQueryParamEntryField();
pCol = &(pDoc->maTabs[nTab])->aCol[nCol];
}
while (!rItem.mbMatchEmpty && pCol->IsEmptyData());
InitPos();
}
if (maCurPos.first->type == sc::element_type_empty)
{
if (rItem.mbMatchEmpty && bSingleQueryItem)
{
// This shortcut, instead of determining if any SC_OR query
// exists or this query is SC_AND'ed (which wouldn't make
// sense, but..) and evaluating them in ValidQuery(), is
// possible only because the interpreter is the only caller
// that sets mbMatchEmpty and there is only one item in those
// cases.
// XXX this would have to be reworked if other filters used it
// in different manners and evaluation would have to be done in
// ValidQuery().
count++;
IncPos();
continue;
}
else
{
IncBlock();
continue;
}
}
ScRefCellValue aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
if ( pDoc->maTabs[nTab]->ValidQuery( nRow, maParam,
(nCol == static_cast<SCCOL>(rEntry.nField) ? &aCell : nullptr),
nullptr,
&mrContext) )
{
if (aCell.isEmpty())
return count;
count++;
IncPos();
continue;
}
else
IncPos();
}
return count;
}
void ScCountIfCellIterator::AdvanceQueryParamEntryField()
{
SCSIZE nEntries = maParam.GetEntryCount();
for ( SCSIZE j = 0; j < nEntries; j++ )
{
ScQueryEntry& rEntry = maParam.GetEntry( j );
if ( rEntry.bDoQuery )
{
if ( rEntry.nField < pDoc->MaxCol() )
rEntry.nField++;
else
{
OSL_FAIL( "AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL" );
}
}
else
break; // for
}
}
namespace {
/**
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index 1512471..9069138 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -5810,16 +5810,8 @@ void ScInterpreter::ScCountIf()
}
else
{
ScQueryCellIterator aCellIter(pDok, mrContext, nTab1, rParam, false);
// Keep Entry.nField in iterator on column change
aCellIter.SetAdvanceQueryParamEntryField( true );
if ( aCellIter.GetFirst() )
{
do
{
fCount++;
} while ( aCellIter.GetNext() );
}
ScCountIfCellIterator aCellIter(pDok, mrContext, nTab1, rParam);
fCount += aCellIter.GetCount();
}
}
else