[Libreoffice-commits] core.git: include/o3tl sw/source

Noel Grandin (via logerrit) logerrit at kemper.freedesktop.org
Tue Aug 31 12:27:23 UTC 2021


 include/o3tl/hash_combine.hxx       |   22 ++++++++++
 sw/source/core/inc/fntcache.hxx     |   35 ++++++++++++++--
 sw/source/core/txtnode/fntcache.cxx |   77 +++++++++++-------------------------
 3 files changed, 77 insertions(+), 57 deletions(-)

New commits:
commit bb5425ed3d8cc04e4242059a17912752d6b48c53
Author:     Noel Grandin <noel at peralex.com>
AuthorDate: Sat Aug 28 18:53:07 2021 +0200
Commit:     Noel Grandin <noel.grandin at collabora.co.uk>
CommitDate: Tue Aug 31 14:26:46 2021 +0200

    tdf#135683 speed up writer layout cache access
    
    can be hot, so use a std::unordered_map
    
    Change-Id: I70e34e80cad67536414c71facbe0222dd88c65a7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121208
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.grandin at collabora.co.uk>

diff --git a/include/o3tl/hash_combine.hxx b/include/o3tl/hash_combine.hxx
index 139ee981699c..f56beda62672 100644
--- a/include/o3tl/hash_combine.hxx
+++ b/include/o3tl/hash_combine.hxx
@@ -11,6 +11,17 @@
 
 namespace o3tl
 {
+template <typename T, typename N>
+inline std::enable_if_t<(sizeof(N) == 4)> hash_combine(N& nSeed, T const* pValue, size_t nCount)
+{
+    static_assert(sizeof(nSeed) == 4);
+    for (size_t i = 0; i < nCount; ++i)
+    {
+        nSeed ^= std::hash<T>{}(*pValue) + 0x9E3779B9u + (nSeed << 6) + (nSeed >> 2);
+        ++pValue;
+    }
+}
+
 template <typename T, typename N>
 inline std::enable_if_t<(sizeof(N) == 4)> hash_combine(N& nSeed, T const& nValue)
 {
@@ -18,6 +29,17 @@ inline std::enable_if_t<(sizeof(N) == 4)> hash_combine(N& nSeed, T const& nValue
     nSeed ^= std::hash<T>{}(nValue) + 0x9E3779B9u + (nSeed << 6) + (nSeed >> 2);
 }
 
+template <typename T, typename N>
+inline std::enable_if_t<(sizeof(N) == 8)> hash_combine(N& nSeed, T const* pValue, size_t nCount)
+{
+    static_assert(sizeof(nSeed) == 8);
+    for (size_t i = 0; i < nCount; ++i)
+    {
+        nSeed ^= std::hash<T>{}(*pValue) + 0x9E3779B97F4A7C15llu + (nSeed << 12) + (nSeed >> 4);
+        ++pValue;
+    }
+}
+
 template <typename T, typename N>
 inline std::enable_if_t<(sizeof(N) == 8)> hash_combine(N& nSeed, T const& nValue)
 {
diff --git a/sw/source/core/inc/fntcache.hxx b/sw/source/core/inc/fntcache.hxx
index ab5c6a9e473b..0bdc4757d9b3 100644
--- a/sw/source/core/inc/fntcache.hxx
+++ b/sw/source/core/inc/fntcache.hxx
@@ -23,7 +23,7 @@
 #include <sal/config.h>
 
 #include <cstdint>
-#include <map>
+#include <unordered_map>
 
 #include <vcl/font.hxx>
 #include <vcl/vclptr.hxx>
@@ -60,9 +60,34 @@ void SwClearFntCacheTextGlyphs();
 extern SwFntCache *pFntCache;
 extern SwFntObj *pLastFont;
 
-struct SwTextGlyphsKey;
-bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r);
-struct SwTextGlyphsData;
+/**
+ * Defines a substring on a given output device, to be used as an std::unordered_map<>
+ * key.
+ */
+struct SwTextGlyphsKey
+{
+    VclPtr<OutputDevice> m_pOutputDevice;
+    OUString m_aText;
+    sal_Int32 m_nIndex;
+    sal_Int32 m_nLength;
+    size_t mnHashCode;
+
+    SwTextGlyphsKey(VclPtr<OutputDevice> const& pOutputDevice, const OUString & sText, sal_Int32 nIndex, sal_Int32 nLength);
+    bool operator==(SwTextGlyphsKey const & rhs) const;
+};
+struct SwTextGlyphsKeyHash
+{
+    size_t operator()(SwTextGlyphsKey const & rKey) const { return rKey.mnHashCode; }
+};
+/**
+ * Glyphs and text width for the given SwTextGlyphsKey.
+ */
+struct SwTextGlyphsData
+{
+    SalLayoutGlyphs m_aTextGlyphs;
+    tools::Long m_nTextWidth = -1; // -1 = not computed yet
+};
+typedef std::unordered_map<SwTextGlyphsKey, SwTextGlyphsData, SwTextGlyphsKeyHash> SwTextGlyphsMap;
 
 class SwFntObj final : public SwCacheObj
 {
@@ -86,7 +111,7 @@ class SwFntObj final : public SwCacheObj
     bool m_bPaintBlank : 1;
 
     /// Cache of already calculated layout glyphs and text widths.
-    std::map<SwTextGlyphsKey, SwTextGlyphsData> m_aTextGlyphs;
+    SwTextGlyphsMap m_aTextGlyphs;
 
     static tools::Long s_nPixWidth;
     static MapMode *s_pPixMap;
diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx
index 9490fe51add0..17be5c9f0eef 100644
--- a/sw/source/core/txtnode/fntcache.cxx
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -57,6 +57,7 @@
 #include <strings.hrc>
 #include <fntcap.hxx>
 #include <vcl/outdev/ScopedStates.hxx>
+#include <o3tl/hash_combine.hxx>
 
 using namespace ::com::sun::star;
 
@@ -73,26 +74,31 @@ MapMode* SwFntObj::s_pPixMap = nullptr;
 static vcl::DeleteOnDeinit< VclPtr<OutputDevice> > s_pFntObjPixOut {};
 
 /**
- * Defines a substring on a given output device, to be used as an std::map<>
+ * Defines a substring on a given output device, to be used as an std::unordered_map<>
  * key.
  */
-struct SwTextGlyphsKey
+SwTextGlyphsKey::SwTextGlyphsKey(VclPtr<OutputDevice> const& pOutputDevice, const OUString & sText, sal_Int32 nIndex, sal_Int32 nLength)
+    : m_pOutputDevice(pOutputDevice), m_aText(sText), m_nIndex(nIndex), m_nLength(nLength)
 {
-    VclPtr<OutputDevice> m_pOutputDevice;
-    OUString m_aText;
-    sal_Int32 m_nIndex;
-    sal_Int32 m_nLength;
-
-};
-
-/**
- * Glyphs and text width for the given SwTextGlyphsKey.
- */
-struct SwTextGlyphsData
+    mnHashCode = 0;
+    o3tl::hash_combine(mnHashCode, pOutputDevice.get());
+    o3tl::hash_combine(mnHashCode, m_nIndex);
+    o3tl::hash_combine(mnHashCode, m_nLength);
+    if(m_nLength >= 0 && m_nIndex >= 0 && m_nIndex + m_nLength <= m_aText.getLength())
+        o3tl::hash_combine(mnHashCode, m_aText.getStr() + m_nIndex, m_nLength);
+}
+bool SwTextGlyphsKey::operator==(SwTextGlyphsKey const & rhs) const
 {
-    SalLayoutGlyphs m_aTextGlyphs;
-    tools::Long m_nTextWidth = -1; // -1 = not computed yet
-};
+    bool b = m_pOutputDevice.get() == rhs.m_pOutputDevice.get()
+            && m_nIndex == rhs.m_nIndex
+            && m_nLength == rhs.m_nLength;
+    if (!b)
+        return false;
+    if(m_nLength >= 0 && m_nIndex >= 0 && m_nIndex + m_nLength <= m_aText.getLength())
+        return m_aText.subView(m_nIndex,m_nLength) == rhs.m_aText.subView(m_nIndex, m_nLength);
+    return m_aText == rhs.m_aText;
+}
+
 
 namespace
 {
@@ -118,39 +124,6 @@ tools::Long EvalGridWidthAdd( const SwTextGridItem *const pGrid, const SwDrawTex
 
 }
 
-bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r)
-{
-    if (l.m_pOutputDevice.get() < r.m_pOutputDevice.get())
-        return true;
-    if (l.m_pOutputDevice.get() > r.m_pOutputDevice.get())
-        return false;
-    if (l.m_nIndex < r.m_nIndex)
-        return true;
-    if (l.m_nIndex > r.m_nIndex)
-        return false;
-    if (l.m_nLength < r.m_nLength)
-        return true;
-    if (l.m_nLength > r.m_nLength)
-        return false;
-
-    // Comparing strings is expensive, so compare them:
-    // - only at the end of this function
-    // - only once
-    // - only the relevant substring (if the index/length is not out of bounds)
-    sal_Int32 nRet = 0;
-    if (l.m_nLength < 0 || l.m_nIndex < 0 || l.m_nIndex + l.m_nLength > l.m_aText.getLength())
-        nRet = l.m_aText.compareTo(r.m_aText);
-    else
-        nRet = memcmp(l.m_aText.getStr() + l.m_nIndex, r.m_aText.getStr() + r.m_nIndex,
-                      l.m_nLength * sizeof(sal_Unicode));
-    if (nRet < 0)
-        return true;
-    if (nRet > 0)
-        return false;
-
-    return false;
-};
-
 void SwFntCache::Flush( )
 {
     if ( pLastFont )
@@ -220,7 +193,7 @@ void SwFntObj::CreatePrtFont( const OutputDevice& rPrt )
  * Pre-calculates glyph items for the rendered subset of rKey's text, assuming
  * outdev state does not change between the outdev calls.
  */
-static SalLayoutGlyphs* lcl_CreateLayout(const SwTextGlyphsKey& rKey, std::map<SwTextGlyphsKey, SwTextGlyphsData>::iterator it)
+static SalLayoutGlyphs* lcl_CreateLayout(const SwTextGlyphsKey& rKey, SwTextGlyphsMap::iterator it)
 {
     assert (!it->second.m_aTextGlyphs.IsValid());
 
@@ -243,7 +216,7 @@ static SalLayoutGlyphs* lcl_CreateLayout(const SwTextGlyphsKey& rKey, std::map<S
 
 SalLayoutGlyphs* SwFntObj::GetCachedSalLayoutGlyphs(const SwTextGlyphsKey& key)
 {
-    std::map<SwTextGlyphsKey, SwTextGlyphsData>::iterator it = m_aTextGlyphs.find(key);
+    SwTextGlyphsMap::iterator it = m_aTextGlyphs.find(key);
     if(it != m_aTextGlyphs.end())
     {
         if( it->second.m_aTextGlyphs.IsValid())
@@ -259,7 +232,7 @@ SalLayoutGlyphs* SwFntObj::GetCachedSalLayoutGlyphs(const SwTextGlyphsKey& key)
 
 tools::Long SwFntObj::GetCachedTextWidth(const SwTextGlyphsKey& key, const vcl::text::TextLayoutCache* vclCache)
 {
-    std::map<SwTextGlyphsKey, SwTextGlyphsData>::iterator it = m_aTextGlyphs.find(key);
+    SwTextGlyphsMap::iterator it = m_aTextGlyphs.find(key);
     if(it != m_aTextGlyphs.end() && it->second.m_nTextWidth >= 0)
         return it->second.m_nTextWidth;
     if(it == m_aTextGlyphs.end())


More information about the Libreoffice-commits mailing list