loolwsd: less aggressive png cache eviction

The overall hit-rate improves significantly
when we don't remove entries too soon.
Here we give entries more time to show their
merrit.

There are now two limits, soft and hard.
The former is used to remove entries with no
hits at all, while the latter is used to
avoid overflowing the cache footprint too
much. Entries also get started with a single
hit, which prevents them getting evicted
too soon (i.e. right after getting added).

Finally, there is no longer need to reduce
the hit count of entries too agressively
since we have two tiers (soft and hard limits)
and zero-hit entries are removed before others.

The average cache size while auto-typing in
Writer doc is less than 180kb, which is 1.4x
the original size of 128kb.

Change-Id: I946318151638c9c64c714190084c492f9098852b
Reviewed-on: https://gerrit.libreoffice.org/31291
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
Tested-by: Ashod Nakashian <ashnakash@gmail.com>
This commit is contained in:
Ashod Nakashian 2016-11-27 00:59:04 -05:00 committed by Ashod Nakashian
parent 9608935a7c
commit ef9ab94580

View file

@ -271,13 +271,15 @@ class PngCache
size_t _hitCount;
CacheData _data;
CacheEntry(size_t defaultSize) :
_hitCount(0),
_hitCount(1), // Every entry is used at least once; prevent removal at birth.
_data( new std::vector< char >() )
{
_data->reserve( defaultSize );
}
} ;
size_t _cacheSize;
static const size_t CacheSizeSoftLimit = (1024 * 4 * 32); // 128k of cache
static const size_t CacheSizeHardLimit = CacheSizeSoftLimit * 2;
size_t _cacheHits;
size_t _cacheTests;
std::map< uint64_t, CacheEntry > _cache;
@ -286,7 +288,7 @@ class PngCache
{
// A normalish PNG image size for text in a writer document is
// around 4k for a content tile, and sub 1k for a background one.
if (_cacheSize > (1024 * 4 * 32) /* 128k of cache */)
if (_cacheSize > CacheSizeHardLimit)
{
size_t avgHits = 0;
for (auto it = _cache.begin(); it != _cache.end(); ++it)
@ -299,14 +301,18 @@ class PngCache
for (auto it = _cache.begin(); it != _cache.end();)
{
if (it->second._hitCount <= avgHits)
if ((_cacheSize > CacheSizeSoftLimit && it->second._hitCount == 0) ||
(_cacheSize > CacheSizeHardLimit && it->second._hitCount > 0 && it->second._hitCount <= avgHits))
{
// Shrink cache when we exceed the size to maximize
// the chance of hitting these entries in the future.
_cacheSize -= it->second._data->size();
it = _cache.erase(it);
}
else
{
it->second._hitCount /= 2;
if (it->second._hitCount > 0)
it->second._hitCount--;
++it;
}
}