chart2/source/view/charttypes/AreaChart.cxx      |    2 
 chart2/source/view/charttypes/VSeriesPlotter.cxx |  125 +++++++----------------
 2 files changed, 42 insertions(+), 85 deletions(-)

New commits:
commit 51ea035a6f1f05260381bf98373774cde7ce5dfa
Author:     Noel Grandin <noelgran...@gmail.com>
AuthorDate: Sat Apr 5 19:41:37 2025 +0200
Commit:     Noel Grandin <noelgran...@gmail.com>
CommitDate: Sun Apr 6 07:12:52 2025 +0200

    tdf#151876 speedup editing large chart
    
    reduces hang time when switching chart to edit mode by 50%.
    
    PerXMinMaxCalculator does not need to build a summary data structure,
    since we already walking over the data in X order.
    Also switch some the intermediate data structures to more cache
    friendly ones. In this case, std::map is better because we walk
    the data in X order.
    
    Change-Id: I3d291fd56becc1dfdd7d2df87d04b11f363bfd52
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183747
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>
    Tested-by: Jenkins

diff --git a/chart2/source/view/charttypes/VSeriesPlotter.cxx 
b/chart2/source/view/charttypes/VSeriesPlotter.cxx
index ecaff62a93ee..bb85d35afffd 100644
--- a/chart2/source/view/charttypes/VSeriesPlotter.cxx
+++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx
@@ -1944,117 +1944,65 @@ namespace {
 class PerXMinMaxCalculator
 {
     typedef std::pair<double, double> MinMaxType;
-    typedef std::map<size_t, MinMaxType> SeriesMinMaxType;
+    typedef std::vector<MinMaxType> SeriesMinMaxType;
     typedef std::map<double, SeriesMinMaxType> GroupMinMaxType;
-    typedef std::unordered_map<double, MinMaxType> TotalStoreType;
     GroupMinMaxType m_SeriesGroup;
+    size_t mnNumSeries;
     size_t mnCurSeries;
 
 public:
-    PerXMinMaxCalculator() : mnCurSeries(0) {}
+    PerXMinMaxCalculator(size_t numSeries) : mnNumSeries(numSeries), 
mnCurSeries(0) {}
 
     void nextSeries() { ++mnCurSeries; }
 
     void setValue(double fX, double fY)
     {
-        SeriesMinMaxType* pStore = getByXValue(fX); // get storage for given X 
value.
-        if (!pStore)
-            // This shouldn't happen!
-            return;
+        SeriesMinMaxType& rStore = m_SeriesGroup[fX]; // get storage for given 
X value.
+        if (rStore.empty())
+            rStore.resize(mnNumSeries, { std::numeric_limits<double>::max(), 
std::numeric_limits<double>::min() });
 
-        SeriesMinMaxType::iterator it = pStore->lower_bound(mnCurSeries);
-        if (it != pStore->end() && !pStore->key_comp()(mnCurSeries, it->first))
-        {
-            MinMaxType& r = it->second;
-            // A min-max pair already exists for this series.  Update it.
-            if (fY < r.first)
-                r.first = fY;
-            if (r.second < fY)
-                r.second = fY;
-        }
-        else
-        {
-            // No existing pair. Insert a new one.
-            pStore->insert(
-                it, SeriesMinMaxType::value_type(
-                    mnCurSeries, MinMaxType(fY,fY)));
-        }
+        MinMaxType& r = rStore[mnCurSeries];
+        if (fY < r.first)
+            r.first = fY;
+        if (r.second < fY)
+            r.second = fY;
     }
 
     void getTotalRange(double& rfMin, double& rfMax) const
     {
-        TotalStoreType aStore;
-        getTotalStore(aStore);
-
-        if (aStore.empty())
+        if (m_SeriesGroup.empty())
         {
             rfMin = std::numeric_limits<double>::quiet_NaN();
             rfMax = std::numeric_limits<double>::quiet_NaN();
             return;
         }
 
-        TotalStoreType::const_iterator it = aStore.begin(), itEnd = 
aStore.end();
-        rfMin = it->second.first;
-        rfMax = it->second.second;
-        for (++it; it != itEnd; ++it)
-        {
-            if (rfMin > it->second.first)
-                rfMin = it->second.first;
-            if (rfMax < it->second.second)
-                rfMax = it->second.second;
-        }
-    }
+        rfMin = std::numeric_limits<double>::max();
+        rfMax = std::numeric_limits<double>::min();
 
-private:
-    /**
-     * Parse all data and reduce them into a set of global Y value ranges per
-     * X value.
-     */
-    void getTotalStore(TotalStoreType& rStore) const
-    {
-        TotalStoreType aStore;
+        /**
+         * For each, X value, calculate Y value range
+         */
         for (auto const& it : m_SeriesGroup)
         {
-            double fX = it.first;
-
             const SeriesMinMaxType& rSeries = it.second;
-            for (auto const& series : rSeries)
+            MinMaxType aPerXMinMax { std::numeric_limits<double>::max(), 0.0 };
+            for (MinMaxType const& series : rSeries)
             {
-                double fYMin = series.second.first, fYMax = 
series.second.second;
-                TotalStoreType::iterator itr = aStore.find(fX);
-                if (itr == aStore.end())
-                    // New min-max pair for give X value.
-                    aStore.emplace(fX, std::pair<double,double>(fYMin,fYMax));
-                else
-                {
-                    MinMaxType& r = itr->second;
-                    if (fYMin < r.first)
-                        r.first = fYMin; // min y-value
+                double fYMin = series.first, fYMax = series.second;
+                if (fYMin < aPerXMinMax.first)
+                    aPerXMinMax.first = fYMin; // min y-value
 
-                    r.second += fYMax; // accumulative max y-value.
-                }
+                aPerXMinMax.second += fYMax; // accumulative max y-value.
             }
-        }
-        rStore.swap(aStore);
-    }
-
-    SeriesMinMaxType* getByXValue(double fX)
-    {
-        GroupMinMaxType::iterator it = m_SeriesGroup.find(fX);
-        if (it == m_SeriesGroup.end())
-        {
-            std::pair<GroupMinMaxType::iterator,bool> r =
-                m_SeriesGroup.insert(std::make_pair(fX, SeriesMinMaxType{}));
 
-            if (!r.second)
-                // insertion failed.
-                return nullptr;
-
-            it = r.first;
+            if (rfMin > aPerXMinMax.first)
+                rfMin = aPerXMinMax.first;
+            if (rfMax < aPerXMinMax.second)
+                rfMax = aPerXMinMax.second;
         }
-
-        return &it->second;
     }
+
 };
 
 }
@@ -2069,7 +2017,17 @@ void 
VDataSeriesGroup::getMinimumAndMaximumYInContinuousXRange(
         // No data series.  Bail out.
         return;
 
-    PerXMinMaxCalculator aRangeCalc;
+    size_t nNumSeries = 0;
+    for (const std::unique_ptr<VDataSeries> & pSeries : m_aSeriesVector)
+    {
+        if (!pSeries)
+            continue;
+        if (nAxisIndex != pSeries->getAttachedAxisIndex())
+            continue;
+        nNumSeries++;
+    }
+
+    PerXMinMaxCalculator aRangeCalc(nNumSeries);
     for (const std::unique_ptr<VDataSeries> & pSeries : m_aSeriesVector)
     {
         if (!pSeries)
commit a35e809943ec9a06a94cff3b11019fb766e8c3a4
Author:     Noel Grandin <noelgran...@gmail.com>
AuthorDate: Sat Apr 5 18:30:37 2025 +0200
Commit:     Noel Grandin <noelgran...@gmail.com>
CommitDate: Sun Apr 6 07:12:40 2025 +0200

    hoist calls outside loop in chart2
    
    no need to call these inside the loop
    
    Change-Id: Ica84ca353e3c7b14542c405d8866386210babc3e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183746
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>
    Tested-by: Jenkins

diff --git a/chart2/source/view/charttypes/AreaChart.cxx 
b/chart2/source/view/charttypes/AreaChart.cxx
index f5a73b966c4a..027fd936ee52 100644
--- a/chart2/source/view/charttypes/AreaChart.cxx
+++ b/chart2/source/view/charttypes/AreaChart.cxx
@@ -618,10 +618,10 @@ void AreaChart::createShapes()
                 if (bDateCategory)
                     pSeries->doSortByXValues();
 
+                sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex();
                 for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; 
nIndex++ )
                 {
                     std::map< sal_Int32, double >& rLogicYSumMap = 
aLogicYSumMapByX[nIndex];
-                    sal_Int32 nAttachedAxisIndex = 
pSeries->getAttachedAxisIndex();
                     rLogicYSumMap.insert({nAttachedAxisIndex, 0.0});
 
                     m_pPosHelper = 
&getPlottingPositionHelper(nAttachedAxisIndex);
diff --git a/chart2/source/view/charttypes/VSeriesPlotter.cxx 
b/chart2/source/view/charttypes/VSeriesPlotter.cxx
index 952dd9c7f050..ecaff62a93ee 100644
--- a/chart2/source/view/charttypes/VSeriesPlotter.cxx
+++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx
@@ -2074,12 +2074,11 @@ void 
VDataSeriesGroup::getMinimumAndMaximumYInContinuousXRange(
     {
         if (!pSeries)
             continue;
+        if (nAxisIndex != pSeries->getAttachedAxisIndex())
+            continue;
 
         for (sal_Int32 i = 0, n = pSeries->getTotalPointCount(); i < n; ++i)
         {
-            if (nAxisIndex != pSeries->getAttachedAxisIndex())
-                continue;
-
             double fX = pSeries->getXValue(i);
             if (std::isnan(fX))
                 continue;

Reply via email to