faster hashing of very long strings in SalLayoutGlyphsCache

tdf#147284 being a (pathological) testcase.

Change-Id: I08d8dffb40193b461555bed818c040761e8d575b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132669
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
This commit is contained in:
Luboš Luňák
2022-04-06 19:28:07 +02:00
parent 1b593dbc62
commit 94c2fb28d7
3 changed files with 37 additions and 31 deletions

View File

@@ -20,6 +20,8 @@
#pragma once
#include <sal/types.h>
#include <rtl/ustring.hxx>
#include <o3tl/hash_combine.hxx>
#include <vcl/dllapi.h>
@@ -48,6 +50,36 @@ public:
std::vector<vcl::text::Run> runs;
TextLayoutCache(sal_Unicode const* pStr, sal_Int32 const nEnd);
};
struct FirstCharsStringHash
{
size_t operator()(const OUString& str) const
{
// Strings passed to GenericSalLayout::CreateTextLayoutCache() may be very long,
// and computing an entire hash could almost negate the gain of hashing. Hash just first
// characters, that should be good enough.
size_t hash
= rtl_ustr_hashCode_WithLength(str.getStr(), std::min<size_t>(100, str.getLength()));
o3tl::hash_combine(hash, str.getLength());
return hash;
}
};
struct FastStringCompareEqual
{
bool operator()(const OUString& str1, const OUString& str2) const
{
// Strings passed to GenericSalLayout::CreateTextLayoutCache() may be very long,
// and OUString operator == compares backwards and using hard-written code, while
// memcmp() compares much faster.
if (str1.getLength() != str2.getLength())
return false;
if (str1.getStr() == str2.getStr())
return true;
return memcmp(str1.getStr(), str2.getStr(), str1.getLength() * sizeof(str1.getStr()[0]))
== 0;
}
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View File

@@ -154,40 +154,12 @@ namespace {
return VerticalOrientation(nRet);
}
struct FirstCharsStringHash
{
size_t operator()( const OUString& str ) const
{
// Strings passed to GenericSalLayout::CreateTextLayoutCache() may be very long,
// and computing an entire hash could almost negate the gain of hashing. Hash just first
// characters, that should be good enough.
size_t hash = rtl_ustr_hashCode_WithLength( str.getStr(), std::min<size_t>( 100, str.getLength()));
o3tl::hash_combine(hash, str.getLength());
return hash;
}
};
struct ForwardStringCompareEqual
{
bool operator()( const OUString& str1, const OUString& str2 ) const
{
// Strings passed to GenericSalLayout::CreateTextLayoutCache() may be very long,
// and OUString operator == compares backwards, which is inefficient for very long
// strings (bad memory prefetch).
if( str1.getLength() != str2.getLength())
return false;
if( str1.getStr() == str2.getStr())
return true;
return memcmp( str1.getStr(), str2.getStr(), str1.getLength() * sizeof( str1.getStr()[ 0 ] )) == 0;
}
};
} // namespace
std::shared_ptr<const vcl::text::TextLayoutCache> GenericSalLayout::CreateTextLayoutCache(OUString const& rString)
{
typedef o3tl::lru_map<OUString, std::shared_ptr<const vcl::text::TextLayoutCache>,
FirstCharsStringHash, ForwardStringCompareEqual> Cache;
vcl::text::FirstCharsStringHash, vcl::text::FastStringCompareEqual> Cache;
static vcl::DeleteOnDeinit< Cache > cache( 1000 );
if( Cache* map = cache.get())
{

View File

@@ -21,6 +21,7 @@
#include <vcl/glyphitemcache.hxx>
#include <vcl/vcllayout.hxx>
#include <tools/stream.hxx>
#include <TextLayoutCache.hxx>
SalLayoutGlyphs::SalLayoutGlyphs() {}
@@ -146,7 +147,7 @@ SalLayoutGlyphsCache::CachedGlyphsKey::CachedGlyphsKey(const VclPtr<const Output
SvMemoryStream stream;
WriteFont(stream, outputDevice->GetFont());
o3tl::hash_combine(hashValue, static_cast<const char*>(stream.GetData()), stream.GetSize());
o3tl::hash_combine(hashValue, text);
o3tl::hash_combine(hashValue, vcl::text::FirstCharsStringHash()(text));
o3tl::hash_combine(hashValue, index);
o3tl::hash_combine(hashValue, len);
o3tl::hash_combine(hashValue, logicPos.X());
@@ -158,7 +159,8 @@ inline bool SalLayoutGlyphsCache::CachedGlyphsKey::operator==(const CachedGlyphs
{
return hashValue == other.hashValue && outputDevice == other.outputDevice
&& index == other.index && len == other.len && logicPos == other.logicPos
&& logicWidth == other.logicWidth && text == other.text;
&& logicWidth == other.logicWidth
&& vcl::text::FastStringCompareEqual()(text, other.text);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */