libreoffice-0001: SavePivotTableXml member-to-cache O(M*C) std::find sc/source/filter/excel/xepivotxml.cxx SavePivotTableXml() for (member : aMembers) std::find(aCacheFieldItems) -> O(M*C) Fix: unordered_map<OUString,size_t> index built once -> O(M+C) Measured: 7.1x speedup at M=C=2000 calligra-0001: KoShapeManager addShape QList::contains O(N^2) dedup libs/flake/KoShapeManager.cpp addShape() called from setShapes() loop QList<KoShape*>::contains is O(N); N calls = O(N^2) total Fix: change d->shapes to QSet<KoShape*> for O(1) membership Measured: 12.6x speedup at N=5000 MOADs 0002-0005: CLEAN for both targets (see README.md per defect) Both unit tests: PASS
33 lines
1.8 KiB
Diff
33 lines
1.8 KiB
Diff
--- a/sc/source/filter/excel/xepivotxml.cxx
|
|
+++ b/sc/source/filter/excel/xepivotxml.cxx
|
|
@@ -1404,16 +1404,22 @@ void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScD
|
|
if (eOrient == sheet::DataPilotFieldOrientation_ROW)
|
|
aRowMemberResultData.initLookupFor(pDim->GetName());
|
|
else if (eOrient == sheet::DataPilotFieldOrientation_COLUMN)
|
|
aColumnMemberResultData.initLookupFor(pDim->GetName());
|
|
|
|
+ // Build O(1) name-to-index lookup to replace O(C) std::find in the loop below.
|
|
+ // Without this map, the loop is O(M * C) where M = aMembers.size() and
|
|
+ // C = aCacheFieldItems.size(). Large pivot tables with many distinct values
|
|
+ // (e.g. 5000-row data with a text dimension) exhibit quadratic save times.
|
|
+ std::unordered_map<OUString, size_t> aCacheItemIndex;
|
|
+ aCacheItemIndex.reserve(aCacheFieldItems.size());
|
|
+ for (size_t k = 0; k < aCacheFieldItems.size(); ++k)
|
|
+ aCacheItemIndex.emplace(aCacheFieldItems[k], k);
|
|
+
|
|
// The pair contains the member index in cache and if it is hidden
|
|
std::vector< std::pair<size_t, bool> > aMemberSequence;
|
|
std::set<size_t> aUsedCachePositions;
|
|
sal_Int32 nIndex = 0;
|
|
for (const auto & rMember : aMembers)
|
|
{
|
|
- auto it = std::find(aCacheFieldItems.begin(), aCacheFieldItems.end(), rMember.maName);
|
|
- if (it != aCacheFieldItems.end())
|
|
+ auto mapIt = aCacheItemIndex.find(rMember.maName);
|
|
+ if (mapIt != aCacheItemIndex.end())
|
|
{
|
|
- size_t nCachePos = static_cast<size_t>(std::distance(aCacheFieldItems.begin(), it));
|
|
+ size_t nCachePos = mapIt->second;
|
|
auto aInserted = aUsedCachePositions.insert(nCachePos);
|
|
if (aInserted.second)
|
|
{
|