mirror of
https://git.lyx.org/repos/lyx.git
synced 2025-01-14 12:25:11 +00:00
Improve (modestly) the performance of font metrics caches
This fixes two performance issues and improves the performance of TextMetrics::redoParagraph by 15% in a workload that uses the cache a lot. The difference will be much less when the cache is not used much. 1/ repetion of the hash code computation The code if (cache.contains(key)) result = cache[key]: is not efficient, since qHash(key) has to be computed twice. To fix this a new Cache::object_str() method is added, which allows if (auto * obj = cache.object(key)) result = *obj; 2/ code of has code computation Instead of using a verbose string that is complicated to build as key, new key structs BreakAtKey and TextLayoutKey are introduced, along with the relevant qHash() implementation.
This commit is contained in:
parent
9ae002b69f
commit
6bbd88accf
@ -253,8 +253,8 @@ int GuiFontMetrics::rbearing(char_type c) const
|
|||||||
int GuiFontMetrics::width(docstring const & s) const
|
int GuiFontMetrics::width(docstring const & s) const
|
||||||
{
|
{
|
||||||
PROFILE_THIS_BLOCK(width);
|
PROFILE_THIS_BLOCK(width);
|
||||||
if (strwidth_cache_.contains(s))
|
if (int * wid_p = strwidth_cache_.object_ptr(s))
|
||||||
return strwidth_cache_[s];
|
return *wid_p;
|
||||||
PROFILE_CACHE_MISS(width);
|
PROFILE_CACHE_MISS(width);
|
||||||
/* Several problems have to be taken into account:
|
/* Several problems have to be taken into account:
|
||||||
* * QFontMetrics::width does not returns a wrong value with Qt5 with
|
* * QFontMetrics::width does not returns a wrong value with Qt5 with
|
||||||
@ -328,14 +328,19 @@ int GuiFontMetrics::signedWidth(docstring const & s) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint qHash(TextLayoutKey const & key)
|
||||||
|
{
|
||||||
|
double params = (2 * key.rtl - 1) * key.ws;
|
||||||
|
return std::qHash(key.s) ^ ::qHash(params);
|
||||||
|
}
|
||||||
|
|
||||||
shared_ptr<QTextLayout const>
|
shared_ptr<QTextLayout const>
|
||||||
GuiFontMetrics::getTextLayout(docstring const & s, bool const rtl,
|
GuiFontMetrics::getTextLayout(docstring const & s, bool const rtl,
|
||||||
double const wordspacing) const
|
double const wordspacing) const
|
||||||
{
|
{
|
||||||
PROFILE_THIS_BLOCK(getTextLayout);
|
PROFILE_THIS_BLOCK(getTextLayout);
|
||||||
docstring const s_cache =
|
TextLayoutKey key{s, rtl, wordspacing};
|
||||||
s + (rtl ? "r" : "l") + convert<docstring>(wordspacing);
|
if (auto ptl = qtextlayout_cache_[key])
|
||||||
if (auto ptl = qtextlayout_cache_[s_cache])
|
|
||||||
return ptl;
|
return ptl;
|
||||||
PROFILE_CACHE_MISS(getTextLayout);
|
PROFILE_CACHE_MISS(getTextLayout);
|
||||||
auto const ptl = make_shared<QTextLayout>();
|
auto const ptl = make_shared<QTextLayout>();
|
||||||
@ -368,7 +373,7 @@ GuiFontMetrics::getTextLayout(docstring const & s, bool const rtl,
|
|||||||
ptl->beginLayout();
|
ptl->beginLayout();
|
||||||
ptl->createLine();
|
ptl->createLine();
|
||||||
ptl->endLayout();
|
ptl->endLayout();
|
||||||
qtextlayout_cache_.insert(s_cache, ptl);
|
qtextlayout_cache_.insert(key, ptl);
|
||||||
return ptl;
|
return ptl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,22 +552,27 @@ GuiFontMetrics::breakAt_helper(docstring const & s, int const x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint qHash(BreakAtKey const & key)
|
||||||
|
{
|
||||||
|
int params = key.force + 2 * key.rtl + 4 * key.x;
|
||||||
|
return std::qHash(key.s) ^ ::qHash(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const force) const
|
bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool const force) const
|
||||||
{
|
{
|
||||||
PROFILE_THIS_BLOCK(breakAt);
|
PROFILE_THIS_BLOCK(breakAt);
|
||||||
if (s.empty())
|
if (s.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
docstring const s_cache =
|
BreakAtKey key{s, x, rtl, force};
|
||||||
s + convert<docstring>(x) + (rtl ? "r" : "l") + (force ? "f" : "w");
|
|
||||||
pair<int, int> pp;
|
pair<int, int> pp;
|
||||||
|
if (auto * pp_ptr = breakat_cache_.object_ptr(key))
|
||||||
if (breakat_cache_.contains(s_cache))
|
pp = *pp_ptr;
|
||||||
pp = breakat_cache_[s_cache];
|
|
||||||
else {
|
else {
|
||||||
PROFILE_CACHE_MISS(breakAt);
|
PROFILE_CACHE_MISS(breakAt);
|
||||||
pp = breakAt_helper(s, x, rtl, force);
|
pp = breakAt_helper(s, x, rtl, force);
|
||||||
breakat_cache_.insert(s_cache, pp, s_cache.size() * sizeof(char_type));
|
breakat_cache_.insert(key, pp, sizeof(key) + s.size() * sizeof(char_type));
|
||||||
}
|
}
|
||||||
if (pp.first == -1)
|
if (pp.first == -1)
|
||||||
return false;
|
return false;
|
||||||
|
@ -27,6 +27,32 @@
|
|||||||
namespace lyx {
|
namespace lyx {
|
||||||
namespace frontend {
|
namespace frontend {
|
||||||
|
|
||||||
|
struct BreakAtKey
|
||||||
|
{
|
||||||
|
bool operator==(BreakAtKey const & key) const {
|
||||||
|
return key.s == s && key.x == x && key.rtl == rtl && key.force == force;
|
||||||
|
}
|
||||||
|
|
||||||
|
docstring s;
|
||||||
|
int x;
|
||||||
|
bool rtl;
|
||||||
|
bool force;
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint qHash(BreakAtKey const &);
|
||||||
|
|
||||||
|
struct TextLayoutKey
|
||||||
|
{
|
||||||
|
bool operator==(TextLayoutKey const & key) const {
|
||||||
|
return key.s == s && key.rtl == rtl && key.ws == ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
docstring s;
|
||||||
|
bool rtl;
|
||||||
|
double ws;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class GuiFontMetrics : public FontMetrics
|
class GuiFontMetrics : public FontMetrics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -94,9 +120,9 @@ private:
|
|||||||
/// Cache of string widths
|
/// Cache of string widths
|
||||||
mutable Cache<docstring, int> strwidth_cache_;
|
mutable Cache<docstring, int> strwidth_cache_;
|
||||||
/// Cache for breakAt
|
/// Cache for breakAt
|
||||||
mutable Cache<docstring, std::pair<int, int>> breakat_cache_;
|
mutable Cache<BreakAtKey, std::pair<int, int>> breakat_cache_;
|
||||||
/// Cache for QTextLayout
|
/// Cache for QTextLayout
|
||||||
mutable Cache<docstring, std::shared_ptr<QTextLayout>> qtextlayout_cache_;
|
mutable Cache<TextLayoutKey, std::shared_ptr<QTextLayout>> qtextlayout_cache_;
|
||||||
|
|
||||||
struct AscendDescend {
|
struct AscendDescend {
|
||||||
int ascent;
|
int ascent;
|
||||||
|
@ -56,6 +56,8 @@ public:
|
|||||||
return *obj;
|
return *obj;
|
||||||
return Val();
|
return Val();
|
||||||
}
|
}
|
||||||
|
// Version that returns a pointer, and nullptr if the object is not present
|
||||||
|
Val * object_ptr(Key const & key) const { return Q::object(key); }
|
||||||
/// Synonymous for object, same remark as above.
|
/// Synonymous for object, same remark as above.
|
||||||
Val operator[](Key const & key) const { return object(key); }
|
Val operator[](Key const & key) const { return object(key); }
|
||||||
/// Everything from QCache except QCache::take.
|
/// Everything from QCache except QCache::take.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user