sw/inc/IDocumentMarkAccess.hxx | 7 ++ sw/source/core/doc/CntntIdxStore.cxx | 4 + sw/source/core/doc/docbm.cxx | 84 +++++++++++++++++++++++++++++++++-- sw/source/core/inc/MarkManager.hxx | 1 4 files changed, 92 insertions(+), 4 deletions(-)
New commits: commit 62de2163967c8a55193f126922dc21261644784d Author: Noel Grandin <noel.gran...@collabora.co.uk> AuthorDate: Sat Aug 10 15:18:15 2024 +0200 Commit: Noel Grandin <noel.gran...@collabora.co.uk> CommitDate: Sun Aug 11 08:51:50 2024 +0200 speedup sorting of marks in markmanager when we modify them in ContentIdxStoreImpl, we typically only modify a tiny subset of the marks, so do a partial sort instead of fully sorting the array. Shaves 10% off the load time of a large docx with lots of fieldmarks and bookmarks Change-Id: Ia271e465fb71559b0d4da3242dc4a6e6f07ddc28 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171716 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/sw/inc/IDocumentMarkAccess.hxx b/sw/inc/IDocumentMarkAccess.hxx index e7c98e7a18c2..1f838c9730e0 100644 --- a/sw/inc/IDocumentMarkAccess.hxx +++ b/sw/inc/IDocumentMarkAccess.hxx @@ -207,6 +207,13 @@ class IDocumentMarkAccess virtual void assureSortedMarkContainers() const = 0; + /** + * called when we need to sort a sub-range of the container, elements starting + * at nMinIndexModified where modified. This is used from ContentIdxStoreImpl::RestoreBkmks, + * where we are only modifying a small range at the end of the container. + */ + virtual void assureSortedMarkContainers(sal_Int32 nMinIndexModified) const = 0; + /** returns a STL-like random access iterator to the begin of the sequence of marks. */ virtual const_iterator getAllMarksBegin() const =0; diff --git a/sw/source/core/doc/CntntIdxStore.cxx b/sw/source/core/doc/CntntIdxStore.cxx index 03ee3de02f18..5ef370dbe98b 100644 --- a/sw/source/core/doc/CntntIdxStore.cxx +++ b/sw/source/core/doc/CntntIdxStore.cxx @@ -258,10 +258,12 @@ void ContentIdxStoreImpl::SaveBkmks(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 n void ContentIdxStoreImpl::RestoreBkmks(SwDoc& rDoc, updater_t const & rUpdater) { IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess(); + sal_Int32 nMinIndexModified = SAL_MAX_INT32; for (const MarkEntry& aEntry : m_aBkmkEntries) { if (MarkBase *const pMark = pMarkAccess->getAllMarksBegin()[aEntry.m_nIdx]) { + nMinIndexModified = std::min(nMinIndexModified, sal_Int32(aEntry.m_nIdx)); SwPosition aNewPos(GetRightMarkPos(pMark, aEntry.m_bOther)); rUpdater(aNewPos, aEntry.m_nContent); SetRightMarkPos(pMark, aEntry.m_bOther, &aNewPos); @@ -270,7 +272,7 @@ void ContentIdxStoreImpl::RestoreBkmks(SwDoc& rDoc, updater_t const & rUpdater) if (!m_aBkmkEntries.empty()) { // tdf#105705 sort bookmarks because SaveBkmks special handling of // "bMarkPosEqual" may destroy sort order - pMarkAccess->assureSortedMarkContainers(); + pMarkAccess->assureSortedMarkContainers(nMinIndexModified); } } diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx index 500c81fa0be6..40cde87499fa 100644 --- a/sw/source/core/doc/docbm.cxx +++ b/sw/source/core/doc/docbm.cxx @@ -1801,6 +1801,12 @@ namespace sw::mark const_cast< MarkManager* >(this)->sortMarks(); } + void MarkManager::sortMarks() + { + sort(m_vAllMarks.begin(), m_vAllMarks.end(), &lcl_MarkOrderingByStart<MarkBase>); + sortSubsetMarks(); + } + void MarkManager::sortSubsetMarks() { stable_sort(m_vBookmarks.begin(), m_vBookmarks.end(), &lcl_MarkOrderingByStart<MarkBase>); @@ -1808,10 +1814,82 @@ namespace sw::mark sort(m_vAnnotationMarks.begin(), m_vAnnotationMarks.end(), &lcl_MarkOrderingByStart<MarkBase>); } - void MarkManager::sortMarks() + template<class MarkT> + static void lcl_assureSortedMarkContainers(typename std::vector<MarkT*>& rContainer, + sal_Int32 nMinIndexModified) { - sort(m_vAllMarks.begin(), m_vAllMarks.end(), &lcl_MarkOrderingByStart<MarkBase>); - sortSubsetMarks(); + // We know that the range nMinIndexModified.. has been modified, now we need to extend that range + // to find the total range of elements that need to be sorted. + // We know that the marks have been modified in fairly limited ways, see ContentIdxStoreImpl. + sal_Int32 nMin = nMinIndexModified; + while (nMin != 0) + { + nMin--; + if (rContainer[nMin]->GetMarkStart() < rContainer[nMinIndexModified]->GetMarkStart()) + break; + } + sort(rContainer.begin() + nMin, rContainer.end(), &lcl_MarkOrderingByStart<MarkT>); + } + + template<class MarkT> + static void lcl_assureSortedMarkSubContainers(typename std::vector<MarkT*>& rContainer, + MarkT* pFound) + { + if (pFound) + { + auto it = std::find(rContainer.rbegin(), rContainer.rend(), pFound); + sal_Int32 nFirstModified = std::distance(rContainer.begin(), (it+1).base()); + lcl_assureSortedMarkContainers<MarkT>(rContainer, nFirstModified); + } + } + + /** + * called when we need to sort a sub-range of the container, elements starting + * at nMinIndexModified were modified. This is used from ContentIdxStoreImpl::RestoreBkmks, + * where we are only modifying a small range at the end of the container. + */ + void MarkManager::assureSortedMarkContainers(sal_Int32 nMinIndexModified) const + { + // check if the modified range contains elements from the other sorted containers + Bookmark* pBookmark = nullptr; + Fieldmark* pFieldmark = nullptr; + AnnotationMark* pAnnotationMark = nullptr; + for (auto it = m_vAllMarks.begin() + nMinIndexModified; it != m_vAllMarks.end(); ++it) + { + switch(IDocumentMarkAccess::GetType(**it)) + { + case IDocumentMarkAccess::MarkType::BOOKMARK: + case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK: + case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK: + if (!pBookmark) + pBookmark = static_cast<Bookmark*>(*it); + break; + case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK: + case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK: + case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: + case IDocumentMarkAccess::MarkType::DATE_FIELDMARK: + if (!pFieldmark) + pFieldmark = static_cast<Fieldmark*>(*it); + break; + + case IDocumentMarkAccess::MarkType::ANNOTATIONMARK: + if (!pAnnotationMark) + pAnnotationMark = static_cast<AnnotationMark*>(*it); + break; + + case IDocumentMarkAccess::MarkType::DDE_BOOKMARK: + case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER: + case IDocumentMarkAccess::MarkType::UNO_BOOKMARK: + // no special marks container + break; + } + } + + auto pThis = const_cast<MarkManager*>(this); + lcl_assureSortedMarkContainers<MarkBase>(pThis->m_vAllMarks, nMinIndexModified); + lcl_assureSortedMarkSubContainers<Bookmark>(pThis->m_vBookmarks, pBookmark); + lcl_assureSortedMarkSubContainers<Fieldmark>(pThis->m_vFieldmarks, pFieldmark); + lcl_assureSortedMarkSubContainers<AnnotationMark>(pThis->m_vAnnotationMarks, pAnnotationMark); } template<class MarkT> diff --git a/sw/source/core/inc/MarkManager.hxx b/sw/source/core/inc/MarkManager.hxx index 0b2d8850a201..18886256ba98 100644 --- a/sw/source/core/inc/MarkManager.hxx +++ b/sw/source/core/inc/MarkManager.hxx @@ -123,6 +123,7 @@ namespace sw::mark { virtual std::vector<sw::mark::AnnotationMark*>::const_iterator findFirstAnnotationStartsAfter(const SwPosition& rPos) const override; virtual void assureSortedMarkContainers() const override; + virtual void assureSortedMarkContainers(sal_Int32 nMinIndexModified) const override; typedef std::vector<sw::mark::MarkBase*> container_t;