[calligra/krita-bounded-pooler-kazakov] krita/image: Implemented a new bounded tile data pooler

Dmitry Kazakov dimula73 at gmail.com
Sun Mar 6 20:18:40 CET 2011


Git commit e09e0f0b9db6e95aa8a35c879c2d9b530f48d838 by Dmitry Kazakov.
Committed on 06/03/2011 at 19:38.
Pushed by dkazakov into branch 'krita-bounded-pooler-kazakov'.

Implemented a new bounded tile data pooler

(!) Important notice: it would be more safe to perform 'make clean'
    in krita/image/ directory if you decide to switch to this branch

Well, this commit implements a new memory-bounded pooler thread. The
configuration of the pool is done using a special option
"memoryPoolLimitPercent". It sets the percent of the memory, that you
would like to spend on the tiles pool. Please notice, that this value
is included in hard/soft limits as well. That means Krita will never
take more than Hard Limit memory on tiles and clones together.

This implementation performs not full power yet. I haven't done any
optimizations, so the highest efficiency it can reach now is 50% of
tiles pre-clone hits. I could get this numbers on gradient tool and on
very short brush strokes. It is quite curious, but the longer stroke
you do, the less efficient the pooler is. I hope i'll get it fixed
soon.

CCMAIL:kimageshop at kde.org

M  +19   -4    krita/image/kis_image_config.cpp     
M  +5    -2    krita/image/kis_image_config.h     
M  +1    -0    krita/image/tiles3/kis_tile_data_interface.h     
M  +166  -26   krita/image/tiles3/kis_tile_data_pooler.cc     
M  +19   -3    krita/image/tiles3/kis_tile_data_pooler.h     
M  +33   -8    krita/image/tiles3/kis_tile_data_store.cc     
M  +9    -1    krita/image/tiles3/kis_tile_data_store.h     
M  +2    -7    krita/image/tiles3/swap/kis_tile_data_swapper_p.h     
M  +5    -0    krita/image/tiles3/tests/CMakeLists.txt     
M  +6    -4    krita/image/tiles3/tests/kis_store_limits_test.cpp     
A  +111  -0    krita/image/tiles3/tests/kis_tile_data_pooler_test.cpp         [License: GPL (v2+)]
A  +32   -0    krita/image/tiles3/tests/kis_tile_data_pooler_test.h         [License: GPL (v2+)]

http://commits.kde.org/calligra/e09e0f0b9db6e95aa8a35c879c2d9b530f48d838

diff --git a/krita/image/kis_image_config.cpp b/krita/image/kis_image_config.cpp
index 6911b8c..6724bf3 100644
--- a/krita/image/kis_image_config.cpp
+++ b/krita/image/kis_image_config.cpp
@@ -107,14 +107,19 @@ void KisImageConfig::setSwapWindowSize(int value)
     m_config.writeEntry("swapWindowSize", value);
 }
 
-int KisImageConfig::memoryHardLimit() const
+int KisImageConfig::tilesHardLimit() const
 {
-    return totalRAM() * memoryHardLimitPercent() / 100;
+    return totalRAM() * (memoryHardLimitPercent() - memoryPoolLimitPercent()) / 100;
 }
 
-int KisImageConfig::memorySoftLimit() const
+int KisImageConfig::tilesSoftLimit() const
 {
-    return totalRAM() * memorySoftLimitPercent() / 100;
+    return totalRAM() * (memorySoftLimitPercent() - memoryPoolLimitPercent()) / 100;
+}
+
+int KisImageConfig::poolLimit() const
+{
+    return totalRAM() * memoryPoolLimitPercent() / 100;
 }
 
 qreal KisImageConfig::memoryHardLimitPercent() const
@@ -137,6 +142,16 @@ void KisImageConfig::setMemorySoftLimitPercent(qreal value)
     m_config.writeEntry("memorySoftLimitPercent", value);
 }
 
+qreal KisImageConfig::memoryPoolLimitPercent() const
+{
+    return m_config.readEntry("memoryPoolLimitPercent", 2.);
+}
+
+void KisImageConfig::setMemoryPoolLimitPercent(qreal value)
+{
+    m_config.writeEntry("memoryPoolLimitPercent", value);
+}
+
 
 #if defined Q_OS_LINUX
 #include <sys/sysinfo.h>
diff --git a/krita/image/kis_image_config.h b/krita/image/kis_image_config.h
index 3473ffe..17d1d6e 100644
--- a/krita/image/kis_image_config.h
+++ b/krita/image/kis_image_config.h
@@ -50,13 +50,16 @@ public:
     int swapWindowSize() const;
     void setSwapWindowSize(int value);
 
-    int memoryHardLimit() const; // MiB
-    int memorySoftLimit() const; // MiB
+    int tilesHardLimit() const; // MiB
+    int tilesSoftLimit() const; // MiB
+    int poolLimit() const; // MiB
 
     qreal memoryHardLimitPercent() const; // % of total RAM
     qreal memorySoftLimitPercent() const; // % of total RAM
+    qreal memoryPoolLimitPercent() const; // % of total RAM
     void setMemoryHardLimitPercent(qreal value);
     void setMemorySoftLimitPercent(qreal value);
+    void setMemoryPoolLimitPercent(qreal value);
 
     static int totalRAM(); // MiB
 
diff --git a/krita/image/tiles3/kis_tile_data_interface.h b/krita/image/tiles3/kis_tile_data_interface.h
index f52ae2e..321b5ac 100644
--- a/krita/image/tiles3/kis_tile_data_interface.h
+++ b/krita/image/tiles3/kis_tile_data_interface.h
@@ -169,6 +169,7 @@ private:
     static void freeData(quint8 *ptr, const qint32 pixelSize);
 private:
     friend class KisTileDataPooler;
+    friend class KisTileDataPoolerTest;
     /**
      * A list of pre-duplicated tiledatas.
      * To make a COW faster, KisTileDataPooler thread duplicates
diff --git a/krita/image/tiles3/kis_tile_data_pooler.cc b/krita/image/tiles3/kis_tile_data_pooler.cc
index dd0aaf5..b8e30c7 100644
--- a/krita/image/tiles3/kis_tile_data_pooler.cc
+++ b/krita/image/tiles3/kis_tile_data_pooler.cc
@@ -23,6 +23,8 @@
 #include "kis_tile_data_store_iterators.h"
 #include "kis_debug.h"
 #include "kis_tile_data_pooler.h"
+#include "kis_image_config.h"
+
 
 const qint32 KisTileDataPooler::MAX_NUM_CLONES = 16;
 const qint32 KisTileDataPooler::MAX_TIMEOUT = 60000; // 01m00s
@@ -51,21 +53,52 @@ const qint32 KisTileDataPooler::TIMEOUT_FACTOR = 2;
     } while(0)                                                          \
 
 #define DEBUG_TILE_STATISTICS() debugTileStatistics()
+
+#define DEBUG_LISTS(mem, beggers, beggersMem, donors, donorsMem)        \
+    do {                                                                \
+    qDebug() << "--- getLists finished ---";                            \
+    qDebug() << "  memoryOccupied:" << mem << "/" << m_memoryLimit;     \
+    qDebug() << "  donors:" << donors.size()                            \
+             << "(mem:" << donorsMem << ")";                            \
+    qDebug() << "  beggers:" << beggers.size()                          \
+             << "(mem:" << beggersMem << ")";                           \
+    qDebug() << "--- ----------------- ---";                            \
+    } while(0)
+
+#define DEBUG_ALLOC_CLONE(mem, totalMem)                                \
+        qDebug() << "Alloc mem for clones:" << mem                      \
+                 << "\tMem usage:" << totalMem << "/" << m_memoryLimit
+
+#define DEBUG_FREE_CLONE(freed, demanded)                               \
+            qDebug() << "Freed mem for clones:" << freed                \
+                     << "/" << qAbs(demanded)
+
 #else
 #define DEBUG_CLONE_ACTION(td, numClones)
 #define DEBUG_SIMPLE_ACTION(action)
 #define RUNTIME_SANITY_CHECK(td)
 #define DEBUG_TILE_STATISTICS()
+#define DEBUG_LISTS(mem, beggers, beggersMem, donors, donorsMem)
+#define DEBUG_ALLOC_CLONE(mem, totalMem)
+#define DEBUG_FREE_CLONE(freed, demanded)
 #endif
 
 
-KisTileDataPooler::KisTileDataPooler(KisTileDataStore *store)
-        : QThread()
+KisTileDataPooler::KisTileDataPooler(KisTileDataStore *store, qint32 memoryLimit)
+    : QThread()
 {
     m_shouldExitFlag = 0;
     m_store = store;
     m_timeout = MIN_TIMEOUT;
     m_lastCycleHadWork = false;
+
+    if(memoryLimit >= 0) {
+        m_memoryLimit = memoryLimit;
+    }
+    else {
+        KisImageConfig config;
+        m_memoryLimit = MiB_TO_METRIC(config.poolLimit());
+    }
 }
 
 KisTileDataPooler::~KisTileDataPooler()
@@ -137,19 +170,10 @@ void KisTileDataPooler::waitForWork()
     }
 }
 
-inline bool KisTileDataPooler::interestingTileData(KisTileData* td)
-{
-    /**
-     * We have to look after all clones we created.
-     * That is why we recheck all tiles with non-empty clones lists
-     */
-
-    return td->m_state == KisTileData::NORMAL &&
-           (td->m_usersCount > 1 || !td->m_clonesStack.isEmpty());
-}
-
 void KisTileDataPooler::run()
 {
+    if(!m_memoryLimit) return;
+
     m_shouldExitFlag = false;
 
     while (1) {
@@ -165,19 +189,14 @@ void KisTileDataPooler::run()
 
 
         KisTileDataStoreReverseIterator *iter = m_store->beginReverseIteration();
-        KisTileData *item;
-
-        while(iter->hasNext()) {
-            item = iter->next();
-            if (interestingTileData(item)) {
-
-                qint32 clonesNeeded = numClonesNeeded(item);
-                if (clonesNeeded) {
-                    m_lastCycleHadWork = true;
-                    cloneTileData(item, clonesNeeded);
-                }
-            }
-        }
+        QList<KisTileData*> beggers;
+        QList<KisTileData*> donors;
+        qint32 memoryOccupied;
+
+        getLists(iter, beggers, donors, memoryOccupied);
+
+        m_lastCycleHadWork =
+            processLists(beggers, donors, memoryOccupied);
 
         m_store->endIteration(iter);
 
@@ -187,6 +206,127 @@ void KisTileDataPooler::run()
     }
 }
 
+inline int KisTileDataPooler::clonesMetric(KisTileData *td, int numClones) {
+    return numClones * td->pixelSize();
+}
+
+inline int KisTileDataPooler::clonesMetric(KisTileData *td) {
+    return td->m_clonesStack.size() * td->pixelSize();
+}
+
+inline void KisTileDataPooler::tryFreeOrphanedClones(KisTileData *td)
+{
+    qint32 extraClones = -numClonesNeeded(td);
+
+    if(extraClones > 0) {
+        cloneTileData(td, -extraClones);
+    }
+}
+
+inline qint32 KisTileDataPooler::needMemory(KisTileData *td)
+{
+    qint32 clonesNeeded = !td->age() ? qMax(0, numClonesNeeded(td)) : 0;
+    return clonesMetric(td, clonesNeeded);
+}
+
+inline qint32 KisTileDataPooler::canDonorMemory(KisTileData *td)
+{
+    return td->age() && clonesMetric(td);
+}
+
+template<class Iter>
+void KisTileDataPooler::getLists(Iter *iter,
+                                 QList<KisTileData*> &beggers,
+                                 QList<KisTileData*> &donors,
+                                 qint32 &memoryOccupied)
+{
+    memoryOccupied = 0;
+
+    qint32 needMemoryTotal = 0;
+    qint32 canDonorMemoryTotal = 0;
+
+    qint32 neededMemory;
+    qint32 donoredMemory;
+
+    KisTileData *item;
+
+    while(iter->hasNext()) {
+        item = iter->next();
+
+        tryFreeOrphanedClones(item);
+
+        if((neededMemory = needMemory(item))) {
+            needMemoryTotal += neededMemory;
+            beggers.append(item);
+        }
+        else if((donoredMemory = canDonorMemory(item))) {
+            canDonorMemoryTotal += donoredMemory;
+            donors.append(item);
+        }
+
+        memoryOccupied += clonesMetric(item);
+    }
+
+    DEBUG_LISTS(memoryOccupied,
+                beggers, needMemoryTotal,
+                donors, canDonorMemoryTotal);
+}
+
+qint32 KisTileDataPooler::tryGetMemory(QList<KisTileData*> &donors,
+                                       qint32 memoryMetric)
+{
+    qint32 memoryFreed = 0;
+
+    QMutableListIterator<KisTileData*> iter(donors);
+    iter.toBack();
+
+    while(iter.hasPrevious() && memoryFreed < memoryMetric) {
+        KisTileData *item = iter.previous();
+
+        qint32 numClones = item->m_clonesStack.size();
+        cloneTileData(item, -numClones);
+        memoryFreed += clonesMetric(item, numClones);
+
+        iter.remove();
+    }
+
+    return memoryFreed;
+}
+
+bool KisTileDataPooler::processLists(QList<KisTileData*> &beggers,
+                                     QList<KisTileData*> &donors,
+                                     qint32 memoryOccupied)
+{
+    bool hadWork = false;
+
+
+    foreach(KisTileData *item, beggers) {
+        qint32 clonesNeeded = numClonesNeeded(item);
+        qint32 clonesMemory = clonesMetric(item, clonesNeeded);
+
+        qint32 memoryLeft =
+            m_memoryLimit - (memoryOccupied + clonesMemory);
+
+        if(memoryLeft < 0) {
+            qint32 freedMemory = tryGetMemory(donors, -memoryLeft);
+            memoryOccupied -= freedMemory;
+
+            DEBUG_FREE_CLONE(freedMemory, memoryLeft);
+
+            if(m_memoryLimit < memoryOccupied + clonesMemory)
+                break;
+        }
+
+        cloneTileData(item, clonesNeeded);
+        DEBUG_ALLOC_CLONE(clonesMemory, memoryOccupied);
+
+        memoryOccupied += clonesMemory;
+        hadWork = true;
+    }
+
+    return hadWork;
+}
+
 void KisTileDataPooler::debugTileStatistics()
 {
     /**
diff --git a/krita/image/tiles3/kis_tile_data_pooler.h b/krita/image/tiles3/kis_tile_data_pooler.h
index 99096fc..82a753c 100644
--- a/krita/image/tiles3/kis_tile_data_pooler.h
+++ b/krita/image/tiles3/kis_tile_data_pooler.h
@@ -32,14 +32,13 @@ class KisTileDataPooler : public QThread
 
 public:
 
-    KisTileDataPooler(KisTileDataStore *store);
+    KisTileDataPooler(KisTileDataStore *store, qint32 memoryLimit = -1);
     virtual ~KisTileDataPooler();
 
     void kick();
     void terminatePooler();
 
 protected:
-
     static const qint32 MAX_NUM_CLONES;
     static const qint32 MAX_TIMEOUT;
     static const qint32 MIN_TIMEOUT;
@@ -49,8 +48,24 @@ protected:
     qint32 numClonesNeeded(KisTileData *td) const;
     void cloneTileData(KisTileData *td, qint32 numClones) const;
     void run();
+
+    inline int clonesMetric(KisTileData *td, int numClones);
+    inline int clonesMetric(KisTileData *td);
+
+    inline void tryFreeOrphanedClones(KisTileData *td);
+    inline qint32 needMemory(KisTileData *td);
+    inline qint32 canDonorMemory(KisTileData *td);
+    qint32 tryGetMemory(QList<KisTileData*> &donors, qint32 memoryMetric);
+
+    template<class Iter>
+        void getLists(Iter *iter, QList<KisTileData*> &beggers,
+                      QList<KisTileData*> &donors, qint32 &memoryOccupied);
+
+    bool processLists(QList<KisTileData*> &beggers,
+                      QList<KisTileData*> &donors,
+                      qint32 memoryOccupied);
+
 private:
-    bool interestingTileData(KisTileData* td);
     void debugTileStatistics();
 protected:
     QSemaphore m_semaphore;
@@ -58,6 +73,7 @@ protected:
     KisTileDataStore *m_store;
     qint32 m_timeout;
     bool m_lastCycleHadWork;
+    qint32 m_memoryLimit;
 };
 
 
diff --git a/krita/image/tiles3/kis_tile_data_store.cc b/krita/image/tiles3/kis_tile_data_store.cc
index 20c1dde..140de88 100644
--- a/krita/image/tiles3/kis_tile_data_store.cc
+++ b/krita/image/tiles3/kis_tile_data_store.cc
@@ -42,7 +42,28 @@
 #define DEBUG_FREE_ACTION(td)
 #endif
 
-
+#ifdef DEBUG_HIT_MISS
+qint64 __preclone_miss = 0;
+qint64 __preclone_hit = 0;
+
+qint64 __preclone_miss_user_count = 0;
+qint64 __preclone_miss_age = 0;
+
+#define DEBUG_COUNT_PRECLONE_HIT(td) __preclone_hit++
+#define DEBUG_COUNT_PRECLONE_MISS(td) __preclone_miss++; __preclone_miss_user_count+=td->numUsers(); __preclone_miss_age+=td->age()
+#define DEBUG_REPORT_PRECLONE_EFFICIENCY()                      \
+    qDebug() << "Hits:" << __preclone_hit                       \
+             << "of" << __preclone_hit + __preclone_miss        \
+             << "("                                             \
+             << qreal(__preclone_hit) / (__preclone_hit + __preclone_miss)       \
+             << ")"                                             \
+             << "miss users" << qreal(__preclone_miss_user_count) / __preclone_miss \
+             << "miss age" << qreal(__preclone_miss_age) / __preclone_miss
+#else
+#define DEBUG_COUNT_PRECLONE_HIT(td)
+#define DEBUG_COUNT_PRECLONE_MISS(td)
+#define DEBUG_REPORT_PRECLONE_EFFICIENCY()
+#endif
 
 KisTileDataStore::KisTileDataStore()
     : m_pooler(this),
@@ -51,13 +72,13 @@ KisTileDataStore::KisTileDataStore()
       m_memoryMetric(0)
 {
     m_clockIterator = m_tileDataList.end();
-//    m_pooler.start();
+    m_pooler.start();
     m_swapper.start();
 }
 
 KisTileDataStore::~KisTileDataStore()
 {
-//    m_pooler.terminatePooler();
+    m_pooler.terminatePooler();
     m_swapper.terminateSwapper();
 
     if(numTiles() > 0) {
@@ -126,11 +147,13 @@ KisTileData *KisTileDataStore::duplicateTileData(KisTileData *rhs)
 
     if (rhs->m_clonesStack.pop(td)) {
         DEBUG_PRECLONE_ACTION("+ Pre-clone HIT", rhs, td);
+        DEBUG_COUNT_PRECLONE_HIT(rhs);
     } else {
         rhs->blockSwapping();
         td = new KisTileData(*rhs);
         rhs->unblockSwapping();
         DEBUG_PRECLONE_ACTION("- Pre-clone #MISS#", rhs, td);
+        DEBUG_COUNT_PRECLONE_MISS(rhs);
     }
 
     registerTileData(td);
@@ -231,6 +254,7 @@ void KisTileDataStore::endIteration(KisTileDataStoreReverseIterator* iterator)
 {
     delete iterator;
     m_listLock.unlock();
+    DEBUG_REPORT_PRECLONE_EFFICIENCY();
 }
 
 KisTileDataStoreClockIterator* KisTileDataStore::beginClockIteration()
@@ -276,12 +300,13 @@ void KisTileDataStore::debugClear()
 {
     QMutexLocker lock(&m_listLock);
 
-    KisTileDataListIterator iter = m_tileDataList.begin();
-
-    while(iter != m_tileDataList.end()) {
-        delete *iter;
-        iter = m_tileDataList.erase(iter);
+    foreach(KisTileData *item, m_tileDataList) {
+        delete item;
     }
+
+    m_tileDataList.clear();
+    m_numTiles = 0;
+    m_memoryMetric = 0;
 }
 
 void KisTileDataStore::testingSuspendPooler()
diff --git a/krita/image/tiles3/kis_tile_data_store.h b/krita/image/tiles3/kis_tile_data_store.h
index 470abe4..93178ab 100644
--- a/krita/image/tiles3/kis_tile_data_store.h
+++ b/krita/image/tiles3/kis_tile_data_store.h
@@ -87,7 +87,7 @@ public:
 
     // Called by The Memento Manager after every commit
     inline void kickPooler() {
-//        m_pooler.kick();
+        m_pooler.kick();
 
         //FIXME: maybe, rename a function?
         m_swapper.kick();
@@ -142,6 +142,7 @@ private:
     KisTileDataSwapper m_swapper;
 
     friend class KisTileDataStoreTest;
+    friend class KisTileDataPoolerTest;
     KisSwappedDataStore m_swappedStore;
 
     KisTileDataListIterator m_clockIterator;
@@ -158,5 +159,12 @@ private:
     qint64 m_memoryMetric;
 };
 
+template<typename T>
+inline T MiB_TO_METRIC(T value)
+{
+    unsigned long long __MiB = 1ULL << 20;
+    return value * (__MiB / (KisTileData::WIDTH * KisTileData::HEIGHT));
+}
+
 #endif /* KIS_TILE_DATA_STORE_H_ */
 
diff --git a/krita/image/tiles3/swap/kis_tile_data_swapper_p.h b/krita/image/tiles3/swap/kis_tile_data_swapper_p.h
index 13fe007..81833eb 100644
--- a/krita/image/tiles3/swap/kis_tile_data_swapper_p.h
+++ b/krita/image/tiles3/swap/kis_tile_data_swapper_p.h
@@ -21,11 +21,6 @@
 #include "kis_image_config.h"
 #include "tiles3/kis_tile_data.h"
 
-#define MiB (1ULL << 20)
-
-#define MiB_TO_METRIC(v) (v * (MiB / (KisTileData::WIDTH * KisTileData::HEIGHT)))
-#define METRIC_TO_TILES(metric, pixelSize) (metric / pixelSize)
-
 
 /*
        Limits Diagram
@@ -67,8 +62,8 @@ public:
     KisStoreLimits() {
         KisImageConfig config;
 
-        m_emergencyThreshold = MiB_TO_METRIC(config.memoryHardLimit());
-        m_softLimitThreshold = MiB_TO_METRIC(config.memorySoftLimit());
+        m_emergencyThreshold = MiB_TO_METRIC(config.tilesHardLimit());
+        m_softLimitThreshold = MiB_TO_METRIC(config.tilesSoftLimit());
 
         m_hardLimitThreshold = m_emergencyThreshold - m_emergencyThreshold / 8;
         m_hardLimit = m_hardLimitThreshold - m_hardLimitThreshold / 8;
diff --git a/krita/image/tiles3/tests/CMakeLists.txt b/krita/image/tiles3/tests/CMakeLists.txt
index f1ea808..f5b88be 100644
--- a/krita/image/tiles3/tests/CMakeLists.txt
+++ b/krita/image/tiles3/tests/CMakeLists.txt
@@ -51,3 +51,8 @@ target_link_libraries(KisTileDataStoreTest  ${KDE4_KDEUI_LIBS} kritaimage ${QT_Q
 set(kis_store_limits_test_SRCS kis_store_limits_test.cpp ../kis_tile_data.cc )
 kde4_add_unit_test(KisStoreLimitsTest TESTNAME krita-image-KisStoreLimitsTest  ${kis_store_limits_test_SRCS})
 target_link_libraries(KisStoreLimitsTest  ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
+
+########### next target ###############
+set(kis_tile_data_pooler_test_SRCS kis_tile_data_pooler_test.cpp ../kis_tile_data.cc ../kis_tile_data_pooler.cc )
+kde4_add_unit_test(KisTileDataPoolerTest TESTNAME krita-image-KisTileDataPoolerTest  ${kis_tile_data_pooler_test_SRCS})
+target_link_libraries(KisTileDataPoolerTest  ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})
diff --git a/krita/image/tiles3/tests/kis_store_limits_test.cpp b/krita/image/tiles3/tests/kis_store_limits_test.cpp
index 507f942..25713cd 100644
--- a/krita/image/tiles3/tests/kis_store_limits_test.cpp
+++ b/krita/image/tiles3/tests/kis_store_limits_test.cpp
@@ -26,14 +26,16 @@
 
 void KisStoreLimitsTest::testLimits()
 {
-    const int totalRAM = KisImageConfig::totalRAM();
-    const int halfRAMMetric = MiB_TO_METRIC(totalRAM / 2);
-    const int quarterRAMMetric = MiB_TO_METRIC(totalRAM / 4);
-
     KisImageConfig config;
     config.setMemoryHardLimitPercent(50);
     config.setMemorySoftLimitPercent(25);
+    config.setMemoryPoolLimitPercent(10);
+
+    const int totalRAM = KisImageConfig::totalRAM();
 
+    // values are shifted because of the pooler part
+    const int halfRAMMetric = MiB_TO_METRIC(int(totalRAM * 0.4));
+    const int quarterRAMMetric = MiB_TO_METRIC(int(totalRAM * 0.15));
 
     KisStoreLimits limits;
 
diff --git a/krita/image/tiles3/tests/kis_tile_data_pooler_test.cpp b/krita/image/tiles3/tests/kis_tile_data_pooler_test.cpp
new file mode 100644
index 0000000..b944c3c
--- /dev/null
+++ b/krita/image/tiles3/tests/kis_tile_data_pooler_test.cpp
@@ -0,0 +1,111 @@
+/*
+ *  Copyright (c) 2011 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_tile_data_pooler_test.h"
+#include <qtest_kde.h>
+
+#include "tiles3/kis_tiled_data_manager.h"
+
+#include "tiles3/kis_tile_data_store.h"
+#include "tiles3/kis_tile_data_store_iterators.h"
+
+#include "tiles3/kis_tile_data_pooler.h"
+
+#ifdef DEBUG_TILES
+#define PRETTY_TILE(idx, td)                                    \
+    qDebug() << "tile" << i                                     \
+             << "\tusers" << td->numUsers()                     \
+             << "\tclones" << td->m_clonesStack.size()          \
+             << "\tage" << td->age();
+
+#else
+#define PRETTY_TILE(idx, td)
+#endif
+
+void KisTileDataPoolerTest::testCycles()
+{
+    const qint32 pixelSize = 1;
+    quint8 defaultPixel = 128;
+
+    KisTileDataStore::instance()->debugClear();
+
+    for(int i = 0; i < 12; i++) {
+        KisTileData *td =
+            KisTileDataStore::instance()->createDefaultTileData(pixelSize, &defaultPixel);
+
+        for(int j = 0; j < 1 + (2 - i % 3); j++) {
+            td->acquire();
+        }
+
+        if(!(i/6)) {
+            td->markOld();
+        }
+
+        if(!((i / 3) & 1)) {
+            td->m_clonesStack.push(new KisTileData(*td));
+        }
+
+        PRETTY_TILE(i, td);
+    }
+
+    {
+        KisTileDataPooler pooler(KisTileDataStore::instance(), 5);
+        pooler.start();
+        pooler.kick();
+        pooler.kick();
+
+        QTest::qSleep(500);
+
+        pooler.terminatePooler();
+    }
+
+    int i = 0;
+    KisTileData *item;
+    KisTileDataStoreIterator *iter =
+        KisTileDataStore::instance()->beginIteration();
+
+    while(iter->hasNext()) {
+        item = iter->next();
+
+        int expectedClones;
+
+        switch(i) {
+        case 6:
+        case 7:
+        case 10:
+            expectedClones = 1;
+            break;
+        case 9:
+            expectedClones = 2;
+            break;
+        default:
+            expectedClones = 0;
+        }
+
+        PRETTY_TILE(i, item);
+        QCOMPARE(item->m_clonesStack.size(), expectedClones);
+        i++;
+    }
+
+
+    KisTileDataStore::instance()->endIteration(iter);
+    KisTileDataStore::instance()->debugClear();
+}
+
+QTEST_KDEMAIN(KisTileDataPoolerTest, NoGUI)
+#include "kis_tile_data_pooler_test.moc"
diff --git a/krita/image/tiles3/tests/kis_tile_data_pooler_test.h b/krita/image/tiles3/tests/kis_tile_data_pooler_test.h
new file mode 100644
index 0000000..e5a3785
--- /dev/null
+++ b/krita/image/tiles3/tests/kis_tile_data_pooler_test.h
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (c) 2011 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KIS_TILE_DATA_POOLER_TEST_H
+#define __KIS_TILE_DATA_POOLER_TEST_H
+
+#include <QtTest/QtTest>
+
+class KisTileDataPoolerTest : public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void testCycles();
+};
+
+#endif /* __KIS_TILE_DATA_POOLER_TEST_H */



More information about the kimageshop mailing list