include/o3tl/lru_map.hxx | 14 ++++- sc/inc/conditio.hxx | 13 ++++ sc/source/core/data/conditio.cxx | 102 ++++++++++++++++++++++++++++++++------- 3 files changed, 110 insertions(+), 19 deletions(-)
New commits: commit 8d2354d3b831d483bd44b28e422edc14a704651b Author: Caolán McNamara <[email protected]> AuthorDate: Tue Dec 2 08:21:17 2025 +0000 Commit: Caolán McNamara <[email protected]> CommitDate: Wed Dec 10 21:10:03 2025 +0100 optimize relative conditional formatting effort cache FormulaCells that the relative conditional formatting needs to avoid regenerating them constantly on scrolling. Use something size bounded (lru_map) to avoid potentially creating a massive spreadsheet-sized list of formula cells. Register the cache with the usual cache machinery so they can be dropped on memory pressure/idle. Change-Id: I38dca513791347558225432af0ceda94a7b2742e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194951 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Caolán McNamara <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195363 Tested-by: Caolán McNamara <[email protected]> diff --git a/include/o3tl/lru_map.hxx b/include/o3tl/lru_map.hxx index 65268ea85b4d..64428d568f64 100644 --- a/include/o3tl/lru_map.hxx +++ b/include/o3tl/lru_map.hxx @@ -205,7 +205,7 @@ public: checkLRUMaxSize(); } - void insert(key_value_pair_t& rPair) + list_const_iterator_t insert(key_value_pair_t& rPair) { map_iterator_t i = mLruMap.find(rPair.first); @@ -230,9 +230,11 @@ public: mLruList.splice(mLruList.begin(), mLruList, i->second); checkLRUItemUpdate(); } + + return mLruList.cbegin(); } - void insert(key_value_pair_t&& rPair) + list_const_iterator_t insert(key_value_pair_t&& rPair) { map_iterator_t i = mLruMap.find(rPair.first); @@ -256,6 +258,8 @@ public: mLruList.splice(mLruList.begin(), mLruList, i->second); checkLRUItemUpdate(); } + + return mLruList.cbegin(); } list_const_iterator_t find(const Key& key) @@ -301,6 +305,12 @@ public: return mLruMap.size(); } + size_t empty() const + { + assert(mLruMap.empty() == mLruList.empty()); + return mLruMap.empty(); + } + // size_t total_size() const; - only if custom ValueSize void clear() diff --git a/sc/inc/conditio.hxx b/sc/inc/conditio.hxx index 9a7a3f2f263e..788e19e500bd 100644 --- a/sc/inc/conditio.hxx +++ b/sc/inc/conditio.hxx @@ -34,6 +34,8 @@ #include <rtl/math.hxx> #include <tools/date.hxx> #include <tools/link.hxx> +#include <o3tl/lru_map.hxx> +#include <vcl/dropcache.hxx> #include <optional> #include <map> @@ -301,7 +303,7 @@ public: } }; -class SAL_DLLPUBLIC_RTTI ScConditionEntry : public ScFormatEntry +class SAL_DLLPUBLIC_RTTI ScConditionEntry : public ScFormatEntry, public CacheOwner { // stored data: ScConditionMode eOp; @@ -323,6 +325,9 @@ class SAL_DLLPUBLIC_RTTI ScConditionEntry : public ScFormatEntry OUString aSrcString; // formula source position as text during XML import std::unique_ptr<ScFormulaCell> pFCell1; std::unique_ptr<ScFormulaCell> pFCell2; + typedef o3tl::lru_map<ScAddress, std::unique_ptr<ScFormulaCell>, ScAddressHashFunctor> RelRefCells; + std::unique_ptr<RelRefCells> xRelRefCells1; + std::unique_ptr<RelRefCells> xRelRefCells2; bool bRelRef1; bool bRelRef2; bool bFirstRun; @@ -346,6 +351,8 @@ class SAL_DLLPUBLIC_RTTI ScConditionEntry : public ScFormatEntry bool IsValidStr( const OUString& rArg, const ScAddress& rPos ) const; void StartListening(); + static std::unique_ptr<RelRefCells> makeRelRefCells(); + public: ScConditionEntry( ScConditionMode eOper, const OUString& rExpr1, const OUString& rExpr2, @@ -401,6 +408,10 @@ public: virtual void UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt ) override; virtual void UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt ) override; + virtual bool dropCaches() override; + virtual void dumpState(rtl::OStringBuffer& rState) override; + virtual OUString getCacheName() const override; + bool MarkUsedExternalReferences() const; virtual Type GetType() const override { return eConditionType; } diff --git a/sc/source/core/data/conditio.cxx b/sc/source/core/data/conditio.cxx index dfbeed483166..bc982a0a8c0a 100644 --- a/sc/source/core/data/conditio.cxx +++ b/sc/source/core/data/conditio.cxx @@ -450,7 +450,9 @@ void ScConditionEntry::SetCaseSensitive(bool bSet) void ScConditionEntry::CompileAll() { pFCell1.reset(); + xRelRefCells1.reset(); pFCell2.reset(); + xRelRefCells2.reset(); } void ScConditionEntry::CompileXML() @@ -545,7 +547,11 @@ void ScConditionEntry::UpdateReference( sc::RefUpdateContext& rCxt ) } if (aRes.mbReferenceModified || bChangedPos) - pFCell1.reset(); // is created again in IsValid + { + // is created again in IsValid + pFCell1.reset(); + xRelRefCells1.reset(); + } } if (pFormula2) @@ -564,7 +570,11 @@ void ScConditionEntry::UpdateReference( sc::RefUpdateContext& rCxt ) } if (aRes.mbReferenceModified || bChangedPos) - pFCell2.reset(); // is created again in IsValid + { + // is created again in IsValid + pFCell2.reset(); + xRelRefCells2.reset(); + } } StartListening(); @@ -576,12 +586,14 @@ void ScConditionEntry::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt ) { pFormula1->AdjustReferenceOnInsertedTab(rCxt, aSrcPos); pFCell1.reset(); + xRelRefCells1.reset(); } if (pFormula2) { pFormula2->AdjustReferenceOnInsertedTab(rCxt, aSrcPos); pFCell2.reset(); + xRelRefCells2.reset(); } ScRangeUpdater::UpdateInsertTab(aSrcPos, rCxt); @@ -593,12 +605,14 @@ void ScConditionEntry::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt ) { pFormula1->AdjustReferenceOnDeletedTab(rCxt, aSrcPos); pFCell1.reset(); + xRelRefCells1.reset(); } if (pFormula2) { pFormula2->AdjustReferenceOnDeletedTab(rCxt, aSrcPos); pFCell2.reset(); + xRelRefCells2.reset(); } ScRangeUpdater::UpdateDeleteTab(aSrcPos, rCxt); @@ -615,6 +629,7 @@ void ScConditionEntry::UpdateMoveTab(sc::RefUpdateMoveTabContext& rCxt) if (aRes.mbValueChanged) aResFinal.mnTab = aRes.mnTab; pFCell1.reset(); + xRelRefCells1.reset(); } if (pFormula2) @@ -623,6 +638,7 @@ void ScConditionEntry::UpdateMoveTab(sc::RefUpdateMoveTabContext& rCxt) if (aRes.mbValueChanged) aResFinal.mnTab = aRes.mnTab; pFCell2.reset(); + xRelRefCells2.reset(); } if (aResFinal.mnTab != aSrcPos.Tab()) @@ -669,6 +685,12 @@ bool ScConditionEntry::IsEqual( const ScFormatEntry& rOther, bool bIgnoreSrcPos return true; } +//static +std::unique_ptr<ScConditionEntry::RelRefCells> ScConditionEntry::makeRelRefCells() +{ + return std::make_unique<RelRefCells>(0x9fff); +} + void ScConditionEntry::Interpret( const ScAddress& rPos ) { // Create formula cells @@ -679,16 +701,25 @@ void ScConditionEntry::Interpret( const ScAddress& rPos ) // Evaluate formulas bool bDirty = false; // 1 and 2 separate? - std::optional<ScFormulaCell> oTemp; ScFormulaCell* pEff1 = pFCell1.get(); if ( bRelRef1 ) { - if (pFormula1) - oTemp.emplace(*mpDoc, rPos, *pFormula1); + if (!xRelRefCells1) + xRelRefCells1 = makeRelRefCells(); + auto iFind = xRelRefCells1->find(rPos); + if (iFind != xRelRefCells1->end()) + pEff1 = iFind->second.get(); else - oTemp.emplace(*mpDoc, rPos); - pEff1 = &*oTemp; - pEff1->SetFreeFlying(true); + { + std::unique_ptr<ScFormulaCell> xCell; + if (pFormula1) + xCell = std::make_unique<ScFormulaCell>(*mpDoc, rPos, *pFormula1); + else + xCell = std::make_unique<ScFormulaCell>(*mpDoc, rPos); + xCell->SetFreeFlying(true); + xCell->StartListeningTo(*mpDoc); + pEff1 = xRelRefCells1->insert(std::make_pair(rPos, std::move(xCell)))->second.get(); + } } if ( pEff1 ) { @@ -711,17 +742,26 @@ void ScConditionEntry::Interpret( const ScAddress& rPos ) } } } - oTemp.reset(); ScFormulaCell* pEff2 = pFCell2.get(); //@ 1!=2 if ( bRelRef2 ) { - if (pFormula2) - oTemp.emplace(*mpDoc, rPos, *pFormula2); + if (!xRelRefCells2) + xRelRefCells2 = makeRelRefCells(); + auto iFind = xRelRefCells2->find(rPos); + if (iFind != xRelRefCells2->end()) + pEff2 = iFind->second.get(); else - oTemp.emplace(*mpDoc, rPos); - pEff2 = &*oTemp; - pEff2->SetFreeFlying(true); + { + std::unique_ptr<ScFormulaCell> xCell; + if (pFormula2) + xCell = std::make_unique<ScFormulaCell>(*mpDoc, rPos, *pFormula2); + else + xCell = std::make_unique<ScFormulaCell>(*mpDoc, rPos); + xCell->SetFreeFlying(true); + xCell->StartListeningTo(*mpDoc); + pEff2 = xRelRefCells2->insert(std::make_pair(rPos, std::move(xCell)))->second.get(); + } } if ( pEff2 ) { @@ -743,7 +783,6 @@ void ScConditionEntry::Interpret( const ScAddress& rPos ) } } } - oTemp.reset(); // If IsRunning, the last values remain if (bDirty && !bFirstRun) @@ -1558,16 +1597,47 @@ ScFormatEntry* ScCondFormatEntry::Clone( ScDocument* pDoc ) const void ScConditionEntry::CalcAll() { - if (pFCell1 || pFCell2) + if (pFCell1 || pFCell2 || xRelRefCells1 || !xRelRefCells2) { if (pFCell1) pFCell1->SetDirty(); + if (xRelRefCells1) + { + for (auto& rEntry : *xRelRefCells1) + rEntry.second->SetDirty(); + } if (pFCell2) pFCell2->SetDirty(); + if (xRelRefCells2) + { + for (auto& rEntry : *xRelRefCells2) + rEntry.second->SetDirty(); + } pCondFormat->DoRepaint(); } } +OUString ScConditionEntry::getCacheName() const +{ + return "ScConditionEntry"; +} + +bool ScConditionEntry::dropCaches() +{ + xRelRefCells1.reset(); + xRelRefCells2.reset(); + return true; +} + +void ScConditionEntry::dumpState(rtl::OStringBuffer& rState) +{ + rState.append(" ScConditionEntry:"); + rState.append(" rel ref cells1: "); + rState.append(xRelRefCells1 ? static_cast<sal_Int32>(xRelRefCells1->size()) : 0); + rState.append(" rel ref cells2: "); + rState.append(xRelRefCells2 ? static_cast<sal_Int32>(xRelRefCells2->size()) : 0); +} + ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument* pDoc ) : ScFormatEntry( pDoc ) , meType(condformat::TODAY)
