summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoel Grandin <noel.grandin@collabora.co.uk>2019-11-20 13:46:49 +0200
committerNoel Grandin <noel.grandin@collabora.co.uk>2019-11-21 18:26:50 +0100
commitad7dc47c3d604f5a130e36eedc8ce266fcd84669 (patch)
tree4d53c06734ef8b8b1b40164ca06c679635a7148e
parent35300000346902db38f063b4427a07fdaf6240cd (diff)
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. Reviewed-on: https://gerrit.libreoffice.org/83299 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk> (cherry picked from commit d468958331f36310d11265ba55d7c27366ab58ab) Reviewed-on: https://gerrit.libreoffice.org/83316 Reviewed-by: Xisco FaulĂ­ <xiscofauli@libreoffice.org> (cherry picked from commit e073f996c4ec2582b9560e2fac828c9a73358423) Change-Id: I193a7c181a5dfed6fecf75e871729d73625d0df6 Reviewed-on: https://gerrit.libreoffice.org/83358 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
-rw-r--r--sc/inc/column.hxx1
-rw-r--r--sc/inc/dociter.hxx26
-rw-r--r--sc/inc/document.hxx1
-rw-r--r--sc/inc/table.hxx1
-rw-r--r--sc/source/core/data/dociter.cxx140
-rw-r--r--sc/source/core/tool/interpr1.cxx12
6 files changed, 171 insertions, 10 deletions
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index e7e40fd528bf..940d1ab54428 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 8604abcdd453..d3d3828711cf 100644
--- a/sc/inc/dociter.hxx
+++ b/sc/inc/dociter.hxx
@@ -25,6 +25,7 @@
#include "global.hxx"
#include "scdllapi.h"
#include "cellvalue.hxx"
+#include "queryparam.hxx"
#include "mtvelements.hxx"
#include <vcl/vclptr.hxx>
@@ -365,6 +366,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 ed7b5e2acccb..43351b0e29f4 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -310,6 +310,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 01d809013815..49b0fc8b66da 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 9772d7e82073..9c2fd2d81a6c 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 <= MAXCOL) && (nFoundRow <= 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 < 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 96abd6d9fd18..a624307dedd6 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -5816,16 +5816,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