diff options
author | Noel Grandin <noel.grandin@collabora.co.uk> | 2019-11-20 13:46:49 +0200 |
---|---|---|
committer | Noel Grandin <noel.grandin@collabora.co.uk> | 2019-11-21 18:26:50 +0100 |
commit | ad7dc47c3d604f5a130e36eedc8ce266fcd84669 (patch) | |
tree | 4d53c06734ef8b8b1b40164ca06c679635a7148e | |
parent | 35300000346902db38f063b4427a07fdaf6240cd (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.hxx | 1 | ||||
-rw-r--r-- | sc/inc/dociter.hxx | 26 | ||||
-rw-r--r-- | sc/inc/document.hxx | 1 | ||||
-rw-r--r-- | sc/inc/table.hxx | 1 | ||||
-rw-r--r-- | sc/source/core/data/dociter.cxx | 140 | ||||
-rw-r--r-- | sc/source/core/tool/interpr1.cxx | 12 |
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 |