summaryrefslogtreecommitdiff
path: root/sc
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2020-06-04 18:39:54 +0200
committerLuboš Luňák <l.lunak@collabora.com>2020-06-10 13:20:56 +0200
commit9de418852e852ba1c12503abcd148163d0c51bed (patch)
treef3581576ac4bc3b2062b5c9dc4dfca3723911df2 /sc
parentab128b9d6909fe7799aec3ef34661f8ff0d6a442 (diff)
delay and batch calls to StartListeningFormulaCells() (tdf#133302)
In the testcase ScColumn::StartListeningUnshared() gets called repeatedly with almost the same cells range (just extending), so this gets quadratic. Delaying and doing it once on the whole range at the end avoids this. Change-Id: I3e92817d434cf7e4be0ea658f9adc0a24ceda260 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95531 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lunak@collabora.com> (cherry picked from commit 3677b86af004f331762d460f1a84c950ee8d0480) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95874
Diffstat (limited to 'sc')
-rw-r--r--sc/inc/document.hxx14
-rw-r--r--sc/inc/scopetools.hxx13
-rw-r--r--sc/source/core/data/column.cxx4
-rw-r--r--sc/source/core/data/column3.cxx10
-rw-r--r--sc/source/core/data/documen2.cxx3
-rw-r--r--sc/source/core/data/document10.cxx48
-rw-r--r--sc/source/core/tool/scopetools.cxx22
7 files changed, 112 insertions, 2 deletions
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 6e277f10a284..15a7145b7abc 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -49,6 +49,7 @@
#include <memory>
#include <map>
#include <set>
+#include <unordered_map>
#include <vector>
#include "markdata.hxx"
@@ -129,6 +130,7 @@ class ScBroadcastAreaSlotMachine;
class ScChangeViewSettings;
class ScChartListenerCollection;
class ScClipOptions;
+class ScColumn;
class ScConditionalFormat;
class ScConditionalFormatList;
class ScDBCollection;
@@ -519,6 +521,10 @@ private:
// If the pointer is set, formula cells will not be automatically grouped into shared formula groups,
// instead the range will be extended to contain all such cells.
std::unique_ptr< ScRange > pDelayedFormulaGrouping;
+ // If non-empty, ScColumn::StartListeningFormulaCells() calls may be delayed using this,
+ // avoiding repeated calling for the same cells in the given range. The function will be called once
+ // later for all the cells in the range.
+ std::unordered_map< ScColumn*, std::pair<SCROW, SCROW>> pDelayedStartListeningFormulaCells;
bool bLinkFormulaNeedingCheck; // valid only after loading, for ocDde and ocWebservice
@@ -1360,6 +1366,14 @@ public:
bool IsDelayedFormulaGrouping() const { return pDelayedFormulaGrouping.get() != nullptr; }
/// To be used only by SharedFormulaUtil::joinFormulaCells().
void AddDelayedFormulaGroupingCell( const ScFormulaCell* cell );
+ /// If set, ScColumn::StartListeningFormulaCells() calls may be delayed using
+ /// CanDelayStartListeningFormulaCells() until reset again, at which point the function will
+ /// be called as necessary.
+ void EnableDelayStartListeningFormulaCells( ScColumn* column, bool delay );
+ bool IsEnabledDelayStartListeningFormulaCells( ScColumn* column ) const;
+ /// If true is returned, ScColumn::StartListeningFormulaCells() for the given cells will be performed
+ /// later. If false is returned, it needs to be done explicitly.
+ bool CanDelayStartListeningFormulaCells( ScColumn* column, SCROW row1, SCROW row2 );
FormulaError GetErrCode( const ScAddress& ) const;
diff --git a/sc/inc/scopetools.hxx b/sc/inc/scopetools.hxx
index ac78fb428060..dd060615db67 100644
--- a/sc/inc/scopetools.hxx
+++ b/sc/inc/scopetools.hxx
@@ -13,6 +13,7 @@
#include "scdllapi.h"
class ScDocument;
+class ScColumn;
namespace vcl { class Window; }
namespace sc {
@@ -73,6 +74,18 @@ public:
void reset();
};
+/// Wrapper for ScDocument::EnableDelayStartListeningFormulaCells()
+class DelayStartListeningFormulaCells
+{
+ ScColumn& mColumn;
+ bool const mbOldValue;
+public:
+ DelayStartListeningFormulaCells(ScColumn& column, bool delay);
+ DelayStartListeningFormulaCells(ScColumn& column);
+ ~DelayStartListeningFormulaCells();
+ void set();
+};
+
}
#endif
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index a223352b8dd3..61011da9aeae 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -1661,6 +1661,7 @@ public:
sc::formula_block::const_iterator itEnd = it;
std::advance(itEnd, nDataSize);
+ sc::DelayStartListeningFormulaCells startDelay(mrDestCol); // disabled
if(nDataSize > 1024 && (mnCopyFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE)
{
// If the column to be replaced contains a long formula group (tdf#102364), there can
@@ -1668,6 +1669,9 @@ public:
// the first element becomes very high. Optimize this by removing them in one go.
sc::EndListeningContext context(*mrDestCol.GetDoc());
mrDestCol.EndListeningFormulaCells( context, nRow, nRow + nDataSize - 1, nullptr, nullptr );
+ // There can be a similar problem with starting to listen to cells repeatedly (tdf#133302).
+ // Delay it.
+ startDelay.set();
}
for (; it != itEnd; ++it, ++nRow)
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 88ed96df0f3c..0f4f3d8e76ab 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -349,9 +349,15 @@ void ScColumn::StartListeningUnshared( const std::vector<SCROW>& rNewSharedRows
sc::StartListeningContext aStartCxt(*pDoc, pPosSet);
sc::EndListeningContext aEndCxt(*pDoc, pPosSet);
if (rNewSharedRows.size() >= 2)
- StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[0], rNewSharedRows[1]);
+ {
+ if(!pDoc->CanDelayStartListeningFormulaCells( this, rNewSharedRows[0], rNewSharedRows[1]))
+ StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[0], rNewSharedRows[1]);
+ }
if (rNewSharedRows.size() >= 4)
- StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[2], rNewSharedRows[3]);
+ {
+ if(!pDoc->CanDelayStartListeningFormulaCells( this, rNewSharedRows[2], rNewSharedRows[3]))
+ StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[2], rNewSharedRows[3]);
+ }
}
}
diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx
index 3cbe6374d676..6e52ee8fb8fe 100644
--- a/sc/source/core/data/documen2.cxx
+++ b/sc/source/core/data/documen2.cxx
@@ -394,6 +394,9 @@ ScDocument::~ScDocument()
if(mpCellStringPool.use_count() > 1)
mpCellStringPool->purge();
mpCellStringPool.reset();
+
+ assert( pDelayedFormulaGrouping == nullptr );
+ assert( pDelayedStartListeningFormulaCells.empty());
}
void ScDocument::InitClipPtrs( ScDocument* pSourceDoc )
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx
index b04ac76e06ab..c8156b804c0a 100644
--- a/sc/source/core/data/document10.cxx
+++ b/sc/source/core/data/document10.cxx
@@ -395,6 +395,54 @@ void ScDocument::AddDelayedFormulaGroupingCell( const ScFormulaCell* cell )
pDelayedFormulaGrouping->ExtendTo( cell->aPos );
}
+void ScDocument::EnableDelayStartListeningFormulaCells( ScColumn* column, bool delay )
+{
+ if( delay )
+ {
+ if( pDelayedStartListeningFormulaCells.find( column ) == pDelayedStartListeningFormulaCells.end())
+ pDelayedStartListeningFormulaCells[ column ] = std::pair<SCROW, SCROW>( -1, -1 );
+ }
+ else
+ {
+ auto it = pDelayedStartListeningFormulaCells.find( column );
+ if( it != pDelayedStartListeningFormulaCells.end())
+ {
+ if( it->second.first != -1 )
+ {
+ auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
+ sc::StartListeningContext aStartCxt(*this, pPosSet);
+ sc::EndListeningContext aEndCxt(*this, pPosSet);
+ column->StartListeningFormulaCells(aStartCxt, aEndCxt, it->second.first, it->second.second);
+ }
+ pDelayedStartListeningFormulaCells.erase( it );
+ }
+ }
+}
+
+bool ScDocument::IsEnabledDelayStartListeningFormulaCells( ScColumn* column ) const
+{
+ return pDelayedStartListeningFormulaCells.find( column ) != pDelayedStartListeningFormulaCells.end();
+}
+
+bool ScDocument::CanDelayStartListeningFormulaCells( ScColumn* column, SCROW row1, SCROW row2 )
+{
+ auto it = pDelayedStartListeningFormulaCells.find( column );
+ if( it == pDelayedStartListeningFormulaCells.end())
+ return false; // not enabled
+ if( it->second.first == -1 && it->second.second == -1 ) // uninitialized
+ pDelayedStartListeningFormulaCells[ column ] = std::make_pair( row1, row2 );
+ else
+ {
+ if( row1 > it->second.second + 1 || row2 < it->second.first - 1 )
+ { // two non-adjacent ranges, just bail out
+ return false;
+ }
+ it->second.first = std::min( it->second.first, row1 );
+ it->second.second = std::max( it->second.second, row2 );
+ }
+ return true;
+}
+
bool ScDocument::HasFormulaCell( const ScRange& rRange ) const
{
if (!rRange.IsValid())
diff --git a/sc/source/core/tool/scopetools.cxx b/sc/source/core/tool/scopetools.cxx
index 71a4cf15eb1d..6416e3c2ea89 100644
--- a/sc/source/core/tool/scopetools.cxx
+++ b/sc/source/core/tool/scopetools.cxx
@@ -9,6 +9,7 @@
#include <scopetools.hxx>
#include <document.hxx>
+#include <column.hxx>
#include <vcl/window.hxx>
namespace sc {
@@ -73,6 +74,27 @@ void DelayFormulaGroupingSwitch::reset()
mrDoc.DelayFormulaGrouping(mbOldValue);
}
+DelayStartListeningFormulaCells::DelayStartListeningFormulaCells(ScColumn& column, bool delay)
+ : mColumn(column), mbOldValue(column.GetDoc()->IsEnabledDelayStartListeningFormulaCells(&column))
+{
+ column.GetDoc()->EnableDelayStartListeningFormulaCells(&column, delay);
+}
+
+DelayStartListeningFormulaCells::DelayStartListeningFormulaCells(ScColumn& column)
+ : mColumn(column), mbOldValue(column.GetDoc()->IsEnabledDelayStartListeningFormulaCells(&column))
+{
+}
+
+DelayStartListeningFormulaCells::~DelayStartListeningFormulaCells()
+{
+ mColumn.GetDoc()->EnableDelayStartListeningFormulaCells(&mColumn, mbOldValue);
+}
+
+void DelayStartListeningFormulaCells::set()
+{
+ mColumn.GetDoc()->EnableDelayStartListeningFormulaCells(&mColumn, true);
+}
+
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */