MODIFIED JKQTPDataCache to implement a limited-size cache with least-recently-used delete stategy

This commit is contained in:
jkriege2 2024-01-24 22:33:32 +01:00
parent 868d6dcdf4
commit 1bf6c43134
6 changed files with 241 additions and 32 deletions

View File

@ -97,10 +97,10 @@ This test results in the following numbers (on my AMD Ryzen5 8/16-core laptop):
<b>VERSION:</b> 5.0.0
<b>BUILD MODE:</b> Release
<u><b>SERIAL RESULTS:</b></u><br/>runtime, overall = 2336.7ms<br/>single runtimes = (97.3 +/- 158.4) ms<br/>speedup = 1.00x<br/>threads / available = 1 / 16<br/><br/><br/>
<u><b>SERIAL RESULTS:</b></u><br/>runtime, overall = 4241.3ms<br/>single runtimes = (176.7 +/- 319.6) ms<br/>speedup = 1.00x<br/>threads / available = 1 / 16<br/><br/><br/>
<u><b>PARALLEL RESULTS:</b></u><br/>
runtime, overall = 527.2ms<br/>single runtimes = (166.4 +/- 8.1) ms<br/>speedup = 7.58x<br/>threads / available = 8 / 16<br/>batch runs = 3<br/><br/><b>speedup vs. serial = 4.4x</b>
runtime, overall = 1025.3ms<br/>single runtimes = (325.0 +/- 15.0) ms<br/>speedup = 7.61x<br/>threads / available = 8 / 16<br/>batch runs = 3<br/><br/><b>speedup vs. serial = 4.1x</b>
[comment]:RESULTS_END

View File

@ -25,10 +25,24 @@
#define JKQTPCACHINGTOOLS_H
#include "jkqtcommon/jkqtcommon_imexport.h"
#include "jkqtcommon/jkqtpmathtools.h"
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>
#include <functional>
#include <chrono>
#include <atomic>
#include <algorithm>
#include <unordered_map>
/** \brief tag type to configure JKQTPDataCache for thread-safety
* \ingroup jkqtptools_concurrency
*/
struct JKQTPDataCacheThreadSafe {};
/** \brief tag type to configure JKQTPDataCache for non thread-safety
* \ingroup jkqtptools_concurrency
*/
struct JKQTPDataCacheNotThreadSafe {};
/** \brief this class can be used to implement a general cache for values
* \ingroup jkqtptools_concurrency
@ -45,51 +59,132 @@
*
* Internally the cache maps TKey to TData, but the signature of the get()-function and the generator functor actually uses TKeyInSignature,
* which may differ from TKey. The only limitation is that TKeyInSignature can be converted/assigned to a TKey
*
* The cache has a maximmum size m_maxEntries.
* When you try to add a new object, after which the size would grow beyond this, a fraction 1-m_retainFraction of elements are
* deleted from the cache. The delete strategy is least-recently used (LRU). In order to immplement this, the cache keeps track of
* the last use timestamp of each entry.
*
* You can deactivate the cleaning by setting m_maxEntries<0, but the the cache may grow indefinitely and there is possibly undefined behaviour
* when add one too many items!
*/
template <class TData, class TKey, bool ThreadSafe=true, class TKeyInSignature=TKey>
template <class TData, class TKey, typename ThreadSafe=JKQTPDataCacheThreadSafe, class TKeyInSignature=TKey>
struct JKQTPDataCache {
template <typename FF>
inline JKQTPDataCache(FF generateData):
m_generateData(std::forward<FF>(generateData))
inline JKQTPDataCache(FF generateData, int maxEntries=10000, double retainFraction=0.8):
m_maxEntries(maxEntries), m_retainFraction(retainFraction), m_generateData(std::forward<FF>(generateData))
{
}
JKQTPDataCache()=delete;
JKQTPDataCache(const JKQTPDataCache&)=delete;
JKQTPDataCache& operator=(const JKQTPDataCache&)=delete;
JKQTPDataCache(JKQTPDataCache&&)=default;
JKQTPDataCache& operator=(JKQTPDataCache&&)=default;
template<class... Args>
inline TData get_inline(Args... args) {
return get(TKeyInSignature(args...));
}
template <bool TSS=ThreadSafe>
inline TData get(const typename std::enable_if<TSS, TKeyInSignature>::type& key) {
template <typename TSS=ThreadSafe>
inline TData get(const typename std::enable_if<std::is_same<JKQTPDataCacheThreadSafe, TSS>::value, TKeyInSignature>::type& key) {
const TKey cacheKey=key;
QReadLocker lockR(&m_mutex);
if (m_cache.contains(cacheKey)) return m_cache[cacheKey];
auto it=m_cache.find(cacheKey);
if (m_cache.end()!=it) {
m_cacheLastUseTimestamps[cacheKey]->exchange(currenTimestamp());
return it->second;
}
lockR.unlock();
QWriteLocker lockW(&m_mutex);
if (m_cache.contains(cacheKey)) return m_cache[cacheKey];
it=m_cache.find(cacheKey);
if (m_cache.end()!=it) {
m_cacheLastUseTimestamps.at(cacheKey)->exchange(currenTimestamp());
return it->second;
}
if (m_maxEntries>0 && m_cache.size()>=static_cast<size_t>(m_maxEntries)) cleanCache_notThreadSafe();
m_cacheLastUseTimestamps.emplace(cacheKey, std::make_shared<std::atomic<int64_t>>(currenTimestamp()));
const auto newData=m_generateData(key);
m_cache[cacheKey]=newData;
m_cache.emplace(cacheKey,newData);
return newData;
}
template <bool TSS=ThreadSafe>
inline TData get(const typename std::enable_if<!TSS, TKeyInSignature>::type& key) {
template <typename TSS=ThreadSafe>
inline TData get(const typename std::enable_if<std::is_same<JKQTPDataCacheNotThreadSafe, TSS>::value, TKeyInSignature>::type& key) {
const TKey cacheKey=key;
if (m_cache.contains(cacheKey)) return m_cache[cacheKey];
auto it=m_cache.find(cacheKey);
if (m_cache.end()!=it) {
m_cacheLastUseTimestamps[cacheKey]->exchange(currenTimestamp());
return it->second;
}
if (m_maxEntries>0 && m_cache.size()>=static_cast<size_t>(m_maxEntries)) cleanCache_notThreadSafe();
const auto newData=m_generateData(key);
m_cache[cacheKey]=newData;
m_cache.emplace(cacheKey,newData);
m_cacheLastUseTimestamps.emplace(cacheKey, std::make_shared<std::atomic<int64_t>>(currenTimestamp()));
return newData;
}
template <typename TSS=ThreadSafe>
inline bool contains(const typename std::enable_if<std::is_same<JKQTPDataCacheThreadSafe, TSS>::value, TKeyInSignature>::type& key) const {
const TKey cacheKey=key;
QReadLocker lockR(&m_mutex);
return m_cache.find(cacheKey)!=m_cache.end();
}
template <typename TSS=ThreadSafe>
inline bool contains(const typename std::enable_if<std::is_same<JKQTPDataCacheNotThreadSafe, TSS>::value, TKeyInSignature>::type& key) const {
const TKey cacheKey=key;
return m_cache.find(cacheKey)!=m_cache.end();
}
inline int size() const {
return size_impl<ThreadSafe>();
}
private:
QHash<TKey, TData> m_cache;
mutable typename std::enable_if<ThreadSafe, QReadWriteLock>::type m_mutex;
template <typename TSS>
inline typename std::enable_if<std::is_same<JKQTPDataCacheThreadSafe, TSS>::value, int>::type size_impl() const {
QReadLocker lockR(&m_mutex);
return m_cache.size();
}
template <typename TSS>
inline typename std::enable_if<std::is_same<JKQTPDataCacheNotThreadSafe, TSS>::value, int>::type size_impl() const {
return m_cache.size();
}
/** \brief generate a timestamp */
static inline int64_t currenTimestamp() {
static auto firstTime=std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now()-firstTime).count();
}
/** \brief clean the cache, so at m_retainFraction*m_maxEntries entries remain. */
inline void cleanCache_notThreadSafe() {
if (m_maxEntries<0 || m_cache.size()<static_cast<size_t>(m_maxEntries)) return;
const int deleteItems=jkqtp_boundedRoundTo<int>(1, (1.0-m_retainFraction)*static_cast<double>(m_cache.size()), m_cache.size());
QList<QPair<TKey,int64_t> > allItems;
allItems.reserve(m_cacheLastUseTimestamps.size());
for (auto it=m_cacheLastUseTimestamps.begin(); it!=m_cacheLastUseTimestamps.end(); ++it) {
allItems.emplaceBack(it->first, it->second->load());
}
std::sort(allItems.begin(), allItems.end(), [](const QPair<TKey,int64_t>&a, const QPair<TKey,int64_t>&b) {return a.second>b.second;});
for (int i=0; i<deleteItems; i++) {
m_cache.erase(allItems[i].first);
m_cacheLastUseTimestamps.erase(allItems[i].first);
}
}
const int m_maxEntries;
const double m_retainFraction;
std::unordered_map<TKey, TData> m_cache;
std::unordered_map<TKey, std::shared_ptr<std::atomic<int64_t>>> m_cacheLastUseTimestamps;
mutable QReadWriteLock m_mutex;
const std::function<TData(TKeyInSignature)> m_generateData;
Q_DISABLE_COPY(JKQTPDataCache)
};
#endif // JKQTPCACHINGTOOLS_H

View File

@ -32,6 +32,7 @@
#include <QFont>
#include <QReadWriteLock>
#include <mutex>
#include <functional>
void initJKQTMathTextResources()
@ -884,6 +885,9 @@ QPainterPath JKQTMathTextMakeHBracePath(double x, double ybrace, double width, d
namespace {
struct JKQTMathTextCacheKeyBase {
JKQTMathTextCacheKeyBase():
f(), ldpiX(0), ldpiY(0), pdpiX(0), pdpiY(0)
{}
inline explicit JKQTMathTextCacheKeyBase(const QFont& f_, QPaintDevice *pd):
f(f_)
{
@ -904,7 +908,12 @@ namespace {
inline bool operator==(const JKQTMathTextCacheKeyBase& other) const{
return ldpiX==other.ldpiX && ldpiY==other.ldpiY && pdpiX==other.pdpiX && pdpiY==other.pdpiY && f==other.f;
};
inline bool operator<(const JKQTMathTextCacheKeyBase& other) const{
return ldpiX<other.ldpiX && ldpiY<other.ldpiY && pdpiX<other.pdpiX && pdpiY<other.pdpiY && f<other.f;
};
inline bool operator!=(const JKQTMathTextCacheKeyBase& other) const{
return !operator==(other);
};
};
@ -931,6 +940,9 @@ namespace {
template <class TText=QString>
struct JKQTMathTextTBRDataH: public JKQTMathTextCacheKeyBase {
JKQTMathTextTBRDataH():
JKQTMathTextCacheKeyBase(), text("")
{}
inline explicit JKQTMathTextTBRDataH(const QFont& f_, const TText& text_, QPaintDevice *pd):
JKQTMathTextCacheKeyBase(f_, pd), text(text_)
{
@ -940,7 +952,12 @@ namespace {
inline bool operator==(const JKQTMathTextTBRDataH& other) const{
return JKQTMathTextCacheKeyBase::operator==(other) && text==other.text;
};
inline bool operator!=(const JKQTMathTextTBRDataH& other) const{
return JKQTMathTextCacheKeyBase::operator!=(other) && text!=other.text;
};
inline bool operator<(const JKQTMathTextTBRDataH& other) const{
return JKQTMathTextCacheKeyBase::operator<(other) && text<other.text;
};
};
@ -967,13 +984,41 @@ namespace {
}
template<>
struct std::hash<JKQTMathTextCacheKeyBase>
{
std::size_t operator()(const JKQTMathTextCacheKeyBase& data) const noexcept
{
return qHash(data.f)+std::hash<int>()(data.ldpiX)+std::hash<int>()(data.ldpiY)+std::hash<int>()(data.pdpiX)+std::hash<int>()(data.pdpiY);
}
};
template<>
struct std::hash<JKQTMathTextTBRDataH<QString>>
{
std::size_t operator()(const JKQTMathTextTBRDataH<QString>& data) const noexcept
{
return qHash(data.f)+qHash(data.text)+std::hash<int>()(data.ldpiX)+std::hash<int>()(data.ldpiY)+std::hash<int>()(data.pdpiX)+std::hash<int>()(data.pdpiY);
}
};
template<>
struct std::hash<JKQTMathTextTBRDataH<QChar>>
{
std::size_t operator()(const JKQTMathTextTBRDataH<QChar>& data) const noexcept
{
return qHash(data.f)+qHash(data.text)+std::hash<int>()(data.ldpiX)+std::hash<int>()(data.ldpiY)+std::hash<int>()(data.pdpiX)+std::hash<int>()(data.pdpiY);
}
};
QRectF JKQTMathTextGetTightBoundingRect(const QFont &f, const QString &text, QPaintDevice *pd)
{
//thread_local JKQTPDataCache<QRectF,JKQTMathTextTBRDataH,false,JKQTMathTextTBRDataHExt> cache(
static JKQTPDataCache<QRectF,JKQTMathTextTBRDataH<QString>,true,JKQTMathTextTBRDataHExt<QString>> cache(
static JKQTPDataCache<QRectF,JKQTMathTextTBRDataH<QString>,JKQTPDataCacheThreadSafe,JKQTMathTextTBRDataHExt<QString>> cache(
[](const JKQTMathTextTBRDataHExt<QString>& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.tightBoundingRect(key.text);
@ -986,7 +1031,7 @@ QRectF JKQTMathTextGetTightBoundingRect(const QFont &f, const QString &text, QPa
QRectF JKQTMathTextGetBoundingRect(const QFont &f, const QString &text, QPaintDevice *pd)
{
//thread_local JKQTPDataCache<QRectF,JKQTMathTextTBRDataH,false,JKQTMathTextTBRDataHExt> cache(
static JKQTPDataCache<QRectF,JKQTMathTextTBRDataH<QString>,true,JKQTMathTextTBRDataHExt<QString>> cache(
static JKQTPDataCache<QRectF,JKQTMathTextTBRDataH<QString>,JKQTPDataCacheThreadSafe,JKQTMathTextTBRDataHExt<QString>> cache(
[](const JKQTMathTextTBRDataHExt<QString>& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.boundingRect(key.text);
@ -999,7 +1044,7 @@ QRectF JKQTMathTextGetBoundingRect(const QFont &f, const QString &text, QPaintDe
qreal JKQTMathTextGetHorAdvance(const QFont &f, const QString &text, QPaintDevice *pd)
{
//thread_local JKQTPDataCache<double,JKQTMathTextTBRDataH,false,JKQTMathTextTBRDataHExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextTBRDataH<QString>,true,JKQTMathTextTBRDataHExt<QString>> cache(
static JKQTPDataCache<qreal,JKQTMathTextTBRDataH<QString>,JKQTPDataCacheThreadSafe,JKQTMathTextTBRDataHExt<QString>> cache(
[](const JKQTMathTextTBRDataHExt<QString>& key) {
const QFontMetricsF fm(key.f, key.pd);
#if (QT_VERSION>=QT_VERSION_CHECK(5, 15, 0))
@ -1016,7 +1061,7 @@ qreal JKQTMathTextGetHorAdvance(const QFont &f, const QString &text, QPaintDevic
qreal JKQTMathTextGetRightBearing(const QFont &f, const QChar &text, QPaintDevice *pd)
{
//thread_local JKQTPDataCache<double,JKQTMathTextTBRDataH,false,JKQTMathTextTBRDataHExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextTBRDataH<QChar>,true,JKQTMathTextTBRDataHExt<QChar>> cache(
static JKQTPDataCache<qreal,JKQTMathTextTBRDataH<QChar>,JKQTPDataCacheThreadSafe,JKQTMathTextTBRDataHExt<QChar>> cache(
[](const JKQTMathTextTBRDataHExt<QChar>& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.rightBearing(key.text);
@ -1028,7 +1073,7 @@ qreal JKQTMathTextGetRightBearing(const QFont &f, const QChar &text, QPaintDevic
qreal JKQTMathTextGetLeftBearing(const QFont &f, const QChar &text, QPaintDevice *pd)
{
//thread_local JKQTPDataCache<double,JKQTMathTextTBRDataH,false,JKQTMathTextTBRDataHExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextTBRDataH<QChar>,true,JKQTMathTextTBRDataHExt<QChar>> cache(
static JKQTPDataCache<qreal,JKQTMathTextTBRDataH<QChar>,JKQTPDataCacheThreadSafe,JKQTMathTextTBRDataHExt<QChar>> cache(
[](const JKQTMathTextTBRDataHExt<QChar>& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.leftBearing(key.text);
@ -1040,7 +1085,7 @@ qreal JKQTMathTextGetLeftBearing(const QFont &f, const QChar &text, QPaintDevice
qreal JKQTMathTextGetFontAscent(const QFont &f, QPaintDevice *pd)
{
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,true,JKQTMathTextCacheKeyBaseExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,JKQTPDataCacheThreadSafe,JKQTMathTextCacheKeyBaseExt> cache(
[](const JKQTMathTextCacheKeyBaseExt& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.ascent();
@ -1051,7 +1096,7 @@ qreal JKQTMathTextGetFontAscent(const QFont &f, QPaintDevice *pd)
qreal JKQTMathTextGetFontDescent(const QFont &f, QPaintDevice *pd)
{
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,true,JKQTMathTextCacheKeyBaseExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,JKQTPDataCacheThreadSafe,JKQTMathTextCacheKeyBaseExt> cache(
[](const JKQTMathTextCacheKeyBaseExt& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.descent();
@ -1062,7 +1107,7 @@ qreal JKQTMathTextGetFontDescent(const QFont &f, QPaintDevice *pd)
qreal JKQTMathTextGetFontHeight(const QFont &f, QPaintDevice *pd)
{
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,true,JKQTMathTextCacheKeyBaseExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,JKQTPDataCacheThreadSafe,JKQTMathTextCacheKeyBaseExt> cache(
[](const JKQTMathTextCacheKeyBaseExt& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.height();
@ -1073,7 +1118,7 @@ qreal JKQTMathTextGetFontHeight(const QFont &f, QPaintDevice *pd)
qreal JKQTMathTextGetFontLineWidth(const QFont &f, QPaintDevice *pd)
{
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,true,JKQTMathTextCacheKeyBaseExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,JKQTPDataCacheThreadSafe,JKQTMathTextCacheKeyBaseExt> cache(
[](const JKQTMathTextCacheKeyBaseExt& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.lineWidth();
@ -1084,7 +1129,7 @@ qreal JKQTMathTextGetFontLineWidth(const QFont &f, QPaintDevice *pd)
qreal JKQTMathTextGetFontLeading(const QFont &f, QPaintDevice *pd)
{
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,true,JKQTMathTextCacheKeyBaseExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,JKQTPDataCacheThreadSafe,JKQTMathTextCacheKeyBaseExt> cache(
[](const JKQTMathTextCacheKeyBaseExt& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.leading();
@ -1095,7 +1140,7 @@ qreal JKQTMathTextGetFontLeading(const QFont &f, QPaintDevice *pd)
qreal JKQTMathTextGetFontLineSpacing(const QFont &f, QPaintDevice *pd)
{
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,true,JKQTMathTextCacheKeyBaseExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,JKQTPDataCacheThreadSafe,JKQTMathTextCacheKeyBaseExt> cache(
[](const JKQTMathTextCacheKeyBaseExt& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.lineSpacing();
@ -1106,7 +1151,7 @@ qreal JKQTMathTextGetFontLineSpacing(const QFont &f, QPaintDevice *pd)
qreal JKQTMathTextGetFontStrikoutPos(const QFont &f, QPaintDevice *pd)
{
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,true,JKQTMathTextCacheKeyBaseExt> cache(
static JKQTPDataCache<qreal,JKQTMathTextCacheKeyBase,JKQTPDataCacheThreadSafe,JKQTMathTextCacheKeyBaseExt> cache(
[](const JKQTMathTextCacheKeyBaseExt& key) {
const QFontMetricsF fm(key.f, key.pd);
return fm.strikeOutPos();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 KiB

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -1,6 +1,7 @@
#include <QObject>
#include <QtTest>
#include "jkqtcommon/jkqtpcsstools.h"
#include "jkqtcommon/jkqtpcachingtools.h"
#ifndef QCOMPARE_EQ
#define QCOMPARE_EQ(A,B) if (!static_cast<bool>((A)==(B))) {qDebug()<<QTest::toString(A)<< "!=" << QTest::toString(B); } QVERIFY((A)==(B))
@ -238,6 +239,74 @@ private slots:
QCOMPARE_EQ(n, QColor::fromRgbF(0.52,0.52,0.52,240.0/255.0));
}
inline void test_JKQTPCSSParser_JKQTPDataCache_ThreadSafe() {
JKQTPDataCache<QString, int, JKQTPDataCacheThreadSafe> cache([](int key) { return QString::number(key);}, 100,0.8);
QString sum;
for (int i=0; i<100; i++) {
sum+=cache.get(i);
}
qDebug()<<"sum.size()="<<sum.size();
QCOMPARE_EQ(cache.size(), 100);
for (int i=0; i<100; i++) {
QVERIFY(cache.contains(i));
}
sum+=cache.get(5000);
QCOMPARE_EQ(cache.size(), 81);
for (int i=1000; i<1005; i++) {
sum+=cache.get(i);
}
QCOMPARE_EQ(cache.size(), 86);
qDebug()<<"sum.size()="<<sum.size();
}
inline void test_JKQTPCSSParser_JKQTPDataCache_NotThreadSafe() {
JKQTPDataCache<QString, int, JKQTPDataCacheNotThreadSafe> cache([](int key) { return QString::number(key);}, 100,0.8);
QString sum;
for (int i=0; i<100; i++) {
sum+=cache.get(i);
}
qDebug()<<"sum.size()="<<sum.size();
QCOMPARE_EQ(cache.size(), 100);
for (int i=0; i<100; i++) {
QVERIFY(cache.contains(i));
}
sum+=cache.get(5000);
QCOMPARE_EQ(cache.size(), 81);
for (int i=1000; i<1005; i++) {
sum+=cache.get(i);
}
QCOMPARE_EQ(cache.size(), 86);
qDebug()<<"sum.size()="<<sum.size();
}
inline void benchmark_JKQTPCSSParser_JKQTPDataCache_ThreadSafe() {
JKQTPDataCache<QString, int, JKQTPDataCacheThreadSafe> cache([](int key) { return QString::number(key);}, 100,0.8);
int sum=cache.get(1).size();
QBENCHMARK(sum+=cache.get(1).size());
int i=1;
QBENCHMARK(sum+=cache.get(++i).size());
qDebug()<<"sum.size()="<<sum<<", i="<<i;
}
inline void benchmark_JKQTPCSSParser_JKQTPDataCache_NotThreadSafe() {
JKQTPDataCache<QString, int, JKQTPDataCacheNotThreadSafe> cache([](int key) { return QString::number(key);}, 100,0.8);
int sum=cache.get(1).size();
QBENCHMARK(sum+=cache.get(1).size());
int i=1;
QBENCHMARK(sum+=cache.get(++i).size());
qDebug()<<"sum.size()="<<sum<<", i="<<i;
}
};