formula/source/core/api/token.cxx | 4 +-- include/formula/token.hxx | 41 ++++++++++++++++++++++++++++++++---- sc/source/core/data/formulacell.cxx | 14 ++++++++++++ sc/source/core/inc/interpre.hxx | 3 ++ sc/source/core/tool/interpr4.cxx | 9 ++++++- 5 files changed, 63 insertions(+), 8 deletions(-)
New commits: commit 791a2489eecc3e59d7a31a4bb9e513a576db08ee Author: Caolán McNamara <caolan.mcnam...@collabora.com> AuthorDate: Sun Mar 24 21:31:38 2024 +0000 Commit: Caolán McNamara <caolan.mcnam...@collabora.com> CommitDate: Sat Apr 13 11:54:50 2024 +0200 Related: tdf#160056 elide ref counting for initial tokens The initial tokens provided to interpreting will be restored to their original ref count when interpreting is complete. So we could elide expensive ref counting for them for the duration of the the threaded group calculation. Possibly "temp" tokens that are created just during interpreting could additionally use thread-unsafe ref counts presuming they only appear in a thread specific context. Change-Id: I1f5b0198e83027781be15812680079f28b6a4e27 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165259 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx index 74a212937bb0..b384f091db67 100644 --- a/formula/source/core/api/token.cxx +++ b/formula/source/core/api/token.cxx @@ -55,12 +55,12 @@ static bool lcl_IsReference( OpCode eOp, StackVar eType ) // --- class FormulaToken -------------------------------------------------------- FormulaToken::FormulaToken( StackVar eTypeP, OpCode e ) : - eOp(e), eType( eTypeP ), mnRefCnt(0) + eOp(e), eType( eTypeP ), eRefCntPolicy(RefCntPolicy::ThreadSafe), mnRefCnt(0) { } FormulaToken::FormulaToken( const FormulaToken& r ) : - eOp(r.eOp), eType( r.eType ), mnRefCnt(0) + eOp(r.eOp), eType( r.eType ), eRefCntPolicy(RefCntPolicy::ThreadSafe), mnRefCnt(0) { } diff --git a/include/formula/token.hxx b/include/formula/token.hxx index c729acaceeb6..6fd5670e36b0 100644 --- a/include/formula/token.hxx +++ b/include/formula/token.hxx @@ -120,10 +120,18 @@ inline std::string StackVarEnumToString(StackVar const e) return os.str(); } +enum class RefCntPolicy : sal_uInt8 +{ + ThreadSafe, // refcounting via thread-safe oslInterlockedCount + UnsafeRef, // refcounting done with no locking/guarding against concurrent access + None // no ref counting done +}; + class FORMULA_DLLPUBLIC FormulaToken { OpCode eOp; - const StackVar eType; // type of data + const StackVar eType; // type of data + RefCntPolicy eRefCntPolicy; // style of reference counting mutable oslInterlockedCount mnRefCnt; // reference count FormulaToken& operator=( const FormulaToken& ) = delete; @@ -145,15 +153,40 @@ public: void IncRef() const { - osl_atomic_increment(&mnRefCnt); + switch (eRefCntPolicy) + { + case RefCntPolicy::ThreadSafe: + default: + osl_atomic_increment(&mnRefCnt); + break; + case RefCntPolicy::UnsafeRef: + ++mnRefCnt; + break; + case RefCntPolicy::None: + break; + } } void DecRef() const { - if (!osl_atomic_decrement(&mnRefCnt)) - const_cast<FormulaToken*>(this)->Delete(); + switch (eRefCntPolicy) + { + case RefCntPolicy::ThreadSafe: + default: + if (!osl_atomic_decrement(&mnRefCnt)) + const_cast<FormulaToken*>(this)->Delete(); + break; + case RefCntPolicy::UnsafeRef: + if (!--mnRefCnt) + const_cast<FormulaToken*>(this)->Delete(); + break; + case RefCntPolicy::None: + break; + } } + void SetRefCntPolicy(RefCntPolicy ePolicy) { eRefCntPolicy = ePolicy; } + oslInterlockedCount GetRef() const { return mnRefCnt; } OpCode GetOpCode() const { return eOp; } diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index 16aad6fbcfb3..e57514b0345a 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -5010,6 +5010,13 @@ bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope ScMutationDisable aGuard(rDocument, ScMutationGuardFlags::CORE); + // Here we turn off ref-counting for the contents of pCode on the basis + // that pCode is not modified by interpreting and when interpreting is + // complete all token refcounts will be back to their initial ref count + FormulaToken** pArray = pCode->GetArray(); + for (sal_uInt16 i = 0, n = pCode->GetLen(); i < n; ++i) + pArray[i]->SetRefCntPolicy(RefCntPolicy::None); + // Start nThreadCount new threads std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag(); ScThreadedInterpreterContextGetterGuard aContextGetterGuard(nThreadCount, rDocument, pNonThreadedFormatter); @@ -5031,6 +5038,13 @@ bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope // if they don't get joined from elsewhere before (via ThreadPool::waitUntilDone). rThreadPool.waitUntilDone(aTag, false); + // Drop any caches that reference Tokens before restoring ref counting policy + for (int i = 0; i < nThreadCount; ++i) + aInterpreters[i]->DropTokenCaches(); + + for (sal_uInt16 i = 0, n = pCode->GetLen(); i < n; ++i) + pArray[i]->SetRefCntPolicy(RefCntPolicy::ThreadSafe); + rDocument.SetThreadedGroupCalcInProgress(false); for (int i = 0; i < nThreadCount; ++i) diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx index 81240095fd98..a24edf541977 100644 --- a/sc/source/core/inc/interpre.hxx +++ b/sc/source/core/inc/interpre.hxx @@ -1089,6 +1089,9 @@ public: // Resets the interpreter object, allowing reuse of interpreter object for each cell // in the group. void Init( ScFormulaCell* pCell, const ScAddress& rPos, ScTokenArray& rTokArray ); + // Used only for threaded formula-groups. + // Drops any caches that contain Tokens + void DropTokenCaches(); formula::StackVar Interpret(); diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx index 4408b04aa695..3c4de96e0607 100644 --- a/sc/source/core/tool/interpr4.cxx +++ b/sc/source/core/tool/interpr4.cxx @@ -3887,9 +3887,8 @@ void ScInterpreter::Init( ScFormulaCell* pCell, const ScAddress& rPos, ScTokenAr aCode.ReInit(rTokArray); aPos = rPos; pArr = &rTokArray; - xResult = nullptr; pJumpMatrix = nullptr; - maTokenMatrixMap.clear(); + DropTokenCaches(); pMyFormulaCell = pCell; pCur = nullptr; nGlobalError = FormulaError::NONE; @@ -3906,6 +3905,12 @@ void ScInterpreter::Init( ScFormulaCell* pCell, const ScAddress& rPos, ScTokenAr cPar = 0; } +void ScInterpreter::DropTokenCaches() +{ + xResult = nullptr; + maTokenMatrixMap.clear(); +} + ScCalcConfig& ScInterpreter::GetOrCreateGlobalConfig() { if (!mpGlobalConfig)