Hi there,

        So - poking at some Massif data (kindly generated by Matus) for a large
Calc sheet - with ~300k rows containing ~11 (repeated) formula in each
row - I was interested by the biggest (long term) allocation:

83.61% (1,732,208,505B) (heap allocation functions) malloc/new/new[], 
--alloc-fns, etc.
->20.54% (425,582,672B) 0x16CEE0F3: oox::xls::(anonymous 
namespace)::applyCellFormulas(ScDocumentImport&, oox::xls::(anonymous 
namespace)::CachedTokenArray&, SvNumberFormatter&, 
com::sun::star::uno::Sequence<com::sun::star::sheet::ExternalLinkInfo> const&, 
std::vector<oox::xls::FormulaBuffer::TokenAddressItem, 
std::allocator<oox::xls::FormulaBuffer::TokenAddressItem> > const&) 
(formulabuffer.cxx:210)
| ->20.54% (425,582,672B) 0x16CEE921: oox::xls::FormulaBuffer::finalizeImport() 
(formulabuffer.cxx:307)
|   ->20.54% (425,582,672B) 0x16D59131: 
oox::xls::WorkbookHelper::finalizeWorkbookImport() (workbookhelper.cxx:760)
|     ->20.54% (425,582,672B) 0x16D56250: 
oox::xls::WorkbookFragment::finalizeImport() (workbookfragment.cxx:494)

        Which is essentially 425Mb of:

                new ScFormulaCell(...)

        I had a look at the size of ScFormulaCell which is 152 bytes
which breaks down thus (Linux / 64bit):

        ScFormulaCell           152 bytes
                SvtListener      56 bytes
                ScFormulaResult  16 bytes
                ScAddress         8 bytes
                <fields>         72 bytes.

        The SvtListener is 1/3rd of the size of that. Interestingly we have
~2.8m of these ScFormulaCells in my sample.

        I knocked up the attached patch - assuming that we don't really use
that Listener nearly as much with the new FormulaGroup listener magic -
but ... it turns out that this increases memory usage:

* before
000000000126d000 1758056K 1758056K 1758056K 1758056K      0K rw-p [heap]
0000000001254000 1757728K 1757728K 1757728K 1757728K      0K rw-p [heap]

* after
0000000001478000 1783812K 1783812K 1783812K 1783812K      0K rw-p [heap]
000000000167a000 1782268K 1782268K 1782268K 1782268K      0K rw-p [heap]

        Which is interesting. My hope is that as/when we use the formula-group
listener more effectively, that the ScFormulaCell listener will not
actually be used and this patch will start to have a useful effect :-) I
suspect that we are pushing an entry into it currently for every
single-cell reference.

        My hope is that having a single-cell group listener construct for this
would mean that my tweak might actually work & save a ton of that
duplication / memory allocation. But that's for the future I guess.

        Other things that look a bit wasteful are:

    ScFormulaCell*  pPrevious;
    ScFormulaCell*  pNext;
    ScFormulaCell*  pPreviousTrack;
    ScFormulaCell*  pNextTrack;

        At least one pair of these (IIRC the 'Track') are needed only
transiently during calculation as an append-only list and could be
reasonably easily replaced by a bool bit-field and a std::vector on the
ScDocument itself - which would save 48Mb or so (on 64bit).

        Anyhow - just some thoughts =) I'm dropping this for now myself.

        ATB,

                Michael.

-- 
 michael.me...@collabora.com  <><, Pseudo Engineer, itinerant idiot
From 952bdaf75b996e2cbf86a205e5d944c2f50c7ca1 Mon Sep 17 00:00:00 2001
From: Michael Meeks <michael.me...@collabora.com>
Date: Tue, 23 Dec 2014 09:47:59 +0000
Subject: [PATCH] svl: Make SvtListener more efficient when it has no
 listeners.

boost::unordered_set has a large in-line size: 56 bytes or so and
with the new formula group dependency work, this burns ~30% of the
size of ScFormulaCell which is 20% of our total memory usage. So
instead allocate that as needed on the heap.

Change-Id: I17786516f27e27b4cbc31306bccdaf5dd1f3d017
---
 include/svl/listener.hxx       |  2 +-
 svl/source/notify/listener.cxx | 60 +++++++++++++++++++++++++++++++-----------
 2 files changed, 46 insertions(+), 16 deletions(-)

diff --git a/include/svl/listener.hxx b/include/svl/listener.hxx
index 8b47fda..93fd04e 100644
--- a/include/svl/listener.hxx
+++ b/include/svl/listener.hxx
@@ -29,7 +29,7 @@ class SfxHint;
 class SVL_DLLPUBLIC SvtListener
 {
     typedef boost::unordered_set<SvtBroadcaster*> BroadcastersType;
-    BroadcastersType maBroadcasters;
+    BroadcastersType *mpBroadcasters;
 
     const SvtListener&  operator=(const SvtListener &); // n.i., ist verboten
 
diff --git a/svl/source/notify/listener.cxx b/svl/source/notify/listener.cxx
index 9c2a392..3625a67 100644
--- a/svl/source/notify/listener.cxx
+++ b/svl/source/notify/listener.cxx
@@ -28,10 +28,15 @@ sal_uInt16 SvtListener::QueryBase::getId() const
     return mnId;
 }
 
-SvtListener::SvtListener() {}
+SvtListener::SvtListener() : mpBroadcasters(NULL) {}
 
-SvtListener::SvtListener( const SvtListener &r ) :
-    maBroadcasters(r.maBroadcasters) {}
+SvtListener::SvtListener( const SvtListener &r )
+{
+    if (r.mpBroadcasters)
+        mpBroadcasters = new BroadcastersType(*r.mpBroadcasters);
+    else
+        mpBroadcasters = NULL;
+}
 
 SvtListener::~SvtListener()
 {
@@ -43,8 +48,11 @@ SvtListener::~SvtListener()
 
 bool SvtListener::StartListening( SvtBroadcaster& rBroadcaster )
 {
+    if (!mpBroadcasters)
+        mpBroadcasters = new BroadcastersType();
+
     std::pair<BroadcastersType::iterator, bool> r =
-        maBroadcasters.insert(&rBroadcaster);
+        mpBroadcasters->insert(&rBroadcaster);
     if (r.second)
     {
         // This is a new broadcaster.
@@ -55,48 +63,70 @@ bool SvtListener::StartListening( SvtBroadcaster& rBroadcaster )
 
 bool SvtListener::EndListening( SvtBroadcaster& rBroadcaster )
 {
-    BroadcastersType::iterator it = maBroadcasters.find(&rBroadcaster);
-    if (it == maBroadcasters.end())
+    if (!mpBroadcasters)
+        return false;
+
+    BroadcastersType::iterator it = mpBroadcasters->find(&rBroadcaster);
+    if (it == mpBroadcasters->end())
         // Not listening to this broadcaster.
         return false;
 
     rBroadcaster.Remove(this);
-    maBroadcasters.erase(it);
+    mpBroadcasters->erase(it);
+    if (mpBroadcasters->size() == 0)
+    {
+        delete mpBroadcasters;
+        mpBroadcasters = NULL;
+    }
     return true;
 }
 
 void SvtListener::EndListeningAll()
 {
-    BroadcastersType::iterator it = maBroadcasters.begin(), itEnd = maBroadcasters.end();
+    if (!mpBroadcasters)
+        return;
+
+    BroadcastersType::iterator it = mpBroadcasters->begin(), itEnd = mpBroadcasters->end();
     for (; it != itEnd; ++it)
     {
         SvtBroadcaster& rBC = **it;
         rBC.Remove(this);
     }
-    maBroadcasters.clear();
+    delete mpBroadcasters;
+    mpBroadcasters = NULL;
 }
 
-
 bool SvtListener::IsListening( SvtBroadcaster& rBroadcaster ) const
 {
-    return maBroadcasters.count(&rBroadcaster) > 0;
+    return mpBroadcasters && mpBroadcasters->count(&rBroadcaster) > 0;
 }
 
 void SvtListener::CopyAllBroadcasters( const SvtListener& r )
 {
-    BroadcastersType aCopy(r.maBroadcasters);
-    maBroadcasters.swap(aCopy);
-    BroadcastersType::iterator it = maBroadcasters.begin(), itEnd = maBroadcasters.end();
+    if (!r.mpBroadcasters)
+    {
+        // FIXME: this path, before as now appears to fail
+        // to remove any existing listeners from registered
+        // broadcasters.
+        delete mpBroadcasters;
+        mpBroadcasters = NULL;
+        return;
+    }
+
+    BroadcastersType *pRelease = mpBroadcasters;
+    mpBroadcasters = new BroadcastersType(*r.mpBroadcasters);
+    BroadcastersType::iterator it = mpBroadcasters->begin(), itEnd = mpBroadcasters->end();
     for (; it != itEnd; ++it)
     {
         SvtBroadcaster* p = *it;
         p->Add(this);
     }
+    delete pRelease;
 }
 
 bool SvtListener::HasBroadcaster() const
 {
-    return !maBroadcasters.empty();
+    return mpBroadcasters && !mpBroadcasters->empty();
 }
 
 void SvtListener::Notify( const SfxHint& /*rHint*/ ) {}
-- 
1.8.4.5

_______________________________________________
LibreOffice mailing list
LibreOffice@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/libreoffice

Reply via email to