summaryrefslogtreecommitdiff
path: root/svl
diff options
context:
space:
mode:
authorArmin Le Grand (allotropia) <armin.le.grand.extern@allotropia.de>2023-11-17 17:17:23 +0100
committerArmin Le Grand <Armin.Le.Grand@me.com>2023-11-19 12:57:54 +0100
commitf566a73adcf170d103b0561c7ea2871596af7142 (patch)
tree519bd461e857e3a0c5beef3e34134f7fc59306d6 /svl
parent0c13d8b8d97666a4e23fc93601a0ced141434d78 (diff)
Fix performance regression with ScPatternAttr/SC
Due to the paradigm item change the test make CppunitTest_sc_tablesheetobj with CPPUNIT_TEST_NAME sc_apitest::ScTableSheetObj::testSheetCellRangeProperties got much slower. Unfortunately it did not break, so got unnoted. I took a look now. First I intended to add some hashing in an std::unordered_set using that hash values at ScPatternAttr, but that is not even possible due to other data in that item that needs to be compared. I had the impression that it was 'somehow' hashed before, but after debugging the version before that change I noted that also the list of existing items was linearly compared to the new entry, using the operator==. Thus the problem was not due to not hashing, but due to the ScPatternAttr::operator==. That uses the hash (not changed), but no longer finds equal entries. This is because the hash code is made up from the SfxPoolItemPtrs in the SfxItemSet, so when all are equal we can be sure the SfxItemSet content is equal. To use this the other way around is *not* correct: Even with not all ptrs equal the SfxItemSets can still be equal, simply by one SfxItemSet using another, but identical incarnation of an item. Thuis means that ScPatternAttr::operator== does not detect all cases of equality. This worked in most cases before since most items were 'pooled' and thus much effort was used to ensure their uniqueness, but even before the paradigm item change an item type could be flagged as non-poolable. In that case, this already could fail but with no too bad consequences (just one more copy of an ScPatternAttr would stay). So I fixed that mainly in adapting and optimizing ScPatternAttr::operator==. The results are (same machine, same compiler, dbg version, metioned test): Version before item paradigm change: user 0m50,778s Version after item paradigm change: user 20m13,944s Version with memcmp: user 0m48,845s Version with hash: user 0m48,710s Since that hash does nothing else than to buffer the comparison of those item pointers I just tried to use memcmp instead, as is already used in other places (same file, ScPatternAttr::FastEqualPatternSets, also SfxItemSet::operator==). As can be seen above it makes practically no difference (memcomp even slightly faster). Since that hash is only used in ScPatternAttr::operator== and is same speed (memcomp linearly compares 56 SfxPoolItem ptrs) I decided to remove it. It needs quite some spaces to be reset and re-calculated which are not needed anymore. The calculation is based on dividing the number of items by 4, so we are good with 56, but if someone has/ will adapt the items used by ScPatternAttr it is easy to forget to adapt this, and not easy to change the alghorithm when it's not a multiple of 4. I also optimized/overhauled SfxItemSet::operator== (or better: the SfxItemSet::Equals used by it). It is now better readable, too. I also adapted ScAttrArray::AddCondFormat to not always incarnate/ delete ScPatternAttr instances, only when needed. This also helps a bit and could be done in more places. All in all it is really necessary to cleanup SC's usage of ScPatternAttr - there are quite some possibilities to make that cleaner and faster. In principle it's a huge 'compromize' to use item functionailty to have a possibly 'cheap' maximum shared SfxItemSet at a Cell. Decided to make SfxItemSet::operator== even faster for the case of unequal ranges by iterating over ranges of local SfxItemSet and incremented offset. Still accesses to 2nd SfxItemSet will be the expensive ones using the iterated WhichID. Added two more things to SfxItemSet::operator==: We can return early when both have no items set. For the unequal-ranges compare I added an early-exit when Count() items were compared. Looked at the errors that indeed do trigger the assert in ScPatternAttr::operator== and hint to incarnations of ScPatternAttr that do not use the needed range ATTR_PATTERN_START, ATTR_PATTERN_END. Hunted down to come from ScViewFunc::ApplyAttributes and there from some Dialogs, so seems some SC dialogs do not work with the correct range schema for that item. I tried code in ScViewFunc::ApplyAttributes to fix it, that works. I also tried to hunt that down to the Dialogs that use the wrong schema (TotalCount @SfxItemSet is 61 BTW). While it would be possible to do so, it's no guarentee to have this fixed. Thus I looked at ScPatternAttr::ScPatternAttr and added correciton code when one with the wrong range schema gets created, this is luckily not often needed and transfers the existing items with low costs. Maybe we should add a warning there if used, so at least new implementations of stuff or old ones (the Dialogs) can be corrected? Change-Id: I31da73ae30786bd6c5a08a5e3b7df8fe279af261 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159592 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk> Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
Diffstat (limited to 'svl')
-rw-r--r--svl/source/items/itemset.cxx108
1 files changed, 67 insertions, 41 deletions
diff --git a/svl/source/items/itemset.cxx b/svl/source/items/itemset.cxx
index 8c024ae6a768..6cf4e706e17d 100644
--- a/svl/source/items/itemset.cxx
+++ b/svl/source/items/itemset.cxx
@@ -1482,60 +1482,86 @@ bool SfxItemSet::operator==(const SfxItemSet &rCmp) const
bool SfxItemSet::Equals(const SfxItemSet &rCmp, bool bComparePool) const
{
- // Values we can get quickly need to be the same
- const bool bDifferentPools = (GetPool() != rCmp.GetPool());
- if ( (bComparePool && GetParent() != rCmp.GetParent()) ||
- (bComparePool && bDifferentPools) ||
- Count() != rCmp.Count() )
+ // check if same incarnation
+ if (this == &rCmp)
+ return true;
+
+ // check parents (if requested, also bComparePool)
+ if (bComparePool && GetParent() != rCmp.GetParent())
return false;
- // If we reach here and bDifferentPools==true that means bComparePool==false.
+ // check pools (if requested)
+ if (bComparePool && GetPool() != rCmp.GetPool())
+ return false;
- // Counting Ranges takes longer; they also need to be the same, however
- const sal_uInt16 nCount1(TotalCount());
- const sal_uInt16 nCount2(rCmp.TotalCount());
- if ( nCount1 != nCount2 )
+ // check count of set items
+ if (Count() != rCmp.Count())
return false;
- // Are the Ranges themselves unequal?
- for (sal_Int32 i = 0; i < GetRanges().size(); ++i)
- {
- if (GetRanges()[i] != rCmp.GetRanges()[i])
- {
- // We must use the slow method then
- SfxWhichIter aIter( *this );
- for ( sal_uInt16 nWh = aIter.FirstWhich();
- nWh;
- nWh = aIter.NextWhich() )
- {
- // If the pointer of the shareable Items are unequal, the Items must match
- const SfxPoolItem *pItem1 = nullptr, *pItem2 = nullptr;
- if ( GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWh, false, &pItem1 ) !=
- rCmp.GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWh, false, &pItem2 ) ||
- !SfxPoolItem::areSame(pItem1, pItem2))
- return false;
- }
+ // both have no items, done
+ if (0 == Count())
+ return true;
+ // check if ranges are equal
+ if (GetRanges() == rCmp.GetRanges())
+ {
+ // if yes, we can simplify: are all pointers the same?
+ if (0 == memcmp( m_ppItems, rCmp.m_ppItems, TotalCount() * sizeof(m_ppItems[0]) ))
return true;
+
+ // compare each one separately
+ const SfxPoolItem **ppItem1(m_ppItems);
+ const SfxPoolItem **ppItem2(rCmp.m_ppItems);
+
+ for (sal_uInt16 nPos(0); nPos < TotalCount(); nPos++)
+ {
+ // do full SfxPoolItem compare
+ if (!SfxPoolItem::areSame(*ppItem1, *ppItem2))
+ return false;
+ ++ppItem1;
+ ++ppItem2;
}
- }
- // Are all pointers the same?
- if (0 == memcmp( m_ppItems, rCmp.m_ppItems, nCount1 * sizeof(m_ppItems[0]) ))
return true;
+ }
- // We need to compare each one separately then
- const SfxPoolItem **ppItem1 = m_ppItems;
- const SfxPoolItem **ppItem2 = rCmp.m_ppItems;
- for ( sal_uInt16 nPos = 0; nPos < nCount1; ++nPos )
+ // Not same ranges, need to compare content. Only need to check if
+ // the content of one is inside the other due to already having
+ // compared Count() above.
+ // Iterate over local SfxItemSet by using locval ranges and offset,
+ // so we can access needed data at least for one SfxItemSet more
+ // direct. For the 2nd one we need the WhichID which we have by
+ // iterating over the ranges.
+ sal_uInt16 nOffset(0);
+ sal_uInt16 nNumberToGo(Count());
+
+ for (auto const & rRange : GetRanges())
{
- // If the pointers of the shareable Items are not the same, the Items
- // must match
- if (!SfxPoolItem::areSame(*ppItem1, *ppItem2))
- return false;
+ for (sal_uInt16 nWhich(rRange.first); nWhich <= rRange.second; nWhich++, nOffset++)
+ {
+ const SfxPoolItem *pItem1(nullptr);
+ const SfxPoolItem *pItem2(nullptr);
+ const SfxItemState aStateA(GetItemState_ForOffset(nOffset, &pItem1));
+ const SfxItemState aStateB(rCmp.GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, &pItem2));
+
+ if (aStateA != aStateB)
+ return false;
+
+ // only compare items if SfxItemState::SET, else the item ptrs are not set
+ if (SfxItemState::SET == aStateA && !SfxPoolItem::areSame(pItem1, pItem2))
+ return false;
+
+ if (SfxItemState::DEFAULT != aStateA)
+ // if local item is not-nullptr we have compared one more, reduce NumberToGo
+ // NOTE: we could also use 'nullptr != *(begin() + nOffset)' here, but the
+ // entry was already checked by GetItemState_ForOffset above
+ nNumberToGo--;
- ++ppItem1;
- ++ppItem2;
+ if (0 == nNumberToGo)
+ // if we have compared Count() number of items and are still here
+ // (all were equal), we can exit early
+ return true;
+ }
}
return true;