Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "Test.h"
      9 #include "GrMemoryPool.h"
     10 #include "SkRandom.h"
     11 #include "SkTArray.h"
     12 #include "SkTDArray.h"
     13 #include "SkTemplates.h"
     14 
     15 // A is the top of an inheritance tree of classes that overload op new and
     16 // and delete to use a GrMemoryPool. The objects have values of different types
     17 // that can be set and checked.
     18 class A {
     19 public:
     20     A() {}
     21     virtual void setValues(int v) {
     22         fChar = static_cast<char>(v & 0xFF);
     23     }
     24     virtual bool checkValues(int v) {
     25         return fChar == static_cast<char>(v & 0xFF);
     26     }
     27     virtual ~A() {}
     28 
     29     void* operator new(size_t size) {
     30         if (!gPool.get()) {
     31             return ::operator new(size);
     32         } else {
     33             return gPool->allocate(size);
     34         }
     35     }
     36 
     37     void operator delete(void* p) {
     38         if (!gPool.get()) {
     39             ::operator delete(p);
     40         } else {
     41             return gPool->release(p);
     42         }
     43     }
     44 
     45     static A* Create(SkRandom* r);
     46 
     47     static void SetAllocator(size_t preallocSize, size_t minAllocSize) {
     48         GrMemoryPool* pool = new GrMemoryPool(preallocSize, minAllocSize);
     49         gPool.reset(pool);
     50     }
     51 
     52     static void ResetAllocator() {
     53         gPool.reset(nullptr);
     54     }
     55 
     56 private:
     57     static std::unique_ptr<GrMemoryPool> gPool;
     58     char fChar;
     59 };
     60 
     61 std::unique_ptr<GrMemoryPool> A::gPool;
     62 
     63 class B : public A {
     64 public:
     65     B() {}
     66     virtual void setValues(int v) {
     67         fDouble = static_cast<double>(v);
     68         this->INHERITED::setValues(v);
     69     }
     70     virtual bool checkValues(int v) {
     71         return fDouble == static_cast<double>(v) &&
     72                this->INHERITED::checkValues(v);
     73     }
     74     virtual ~B() {}
     75 
     76 private:
     77     double fDouble;
     78 
     79     typedef A INHERITED;
     80 };
     81 
     82 class C : public A {
     83 public:
     84     C() {}
     85     virtual void setValues(int v) {
     86         fInt64 = static_cast<int64_t>(v);
     87         this->INHERITED::setValues(v);
     88     }
     89     virtual bool checkValues(int v) {
     90         return fInt64 == static_cast<int64_t>(v) &&
     91                this->INHERITED::checkValues(v);
     92     }
     93     virtual ~C() {}
     94 
     95 private:
     96     int64_t fInt64;
     97 
     98     typedef A INHERITED;
     99 };
    100 
    101 // D derives from C and owns a dynamically created B
    102 class D : public C {
    103 public:
    104     D() {
    105         fB = new B();
    106     }
    107     virtual void setValues(int v) {
    108         fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v));
    109         this->INHERITED::setValues(v);
    110         fB->setValues(v);
    111     }
    112     virtual bool checkValues(int v) {
    113         return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) &&
    114                fB->checkValues(v) &&
    115                this->INHERITED::checkValues(v);
    116     }
    117     virtual ~D() {
    118         delete fB;
    119     }
    120 private:
    121     void*   fVoidStar;
    122     B*      fB;
    123 
    124     typedef C INHERITED;
    125 };
    126 
    127 class E : public A {
    128 public:
    129     E() {}
    130     virtual void setValues(int v) {
    131         for (size_t i = 0; i < SK_ARRAY_COUNT(fIntArray); ++i) {
    132             fIntArray[i] = v;
    133         }
    134         this->INHERITED::setValues(v);
    135     }
    136     virtual bool checkValues(int v) {
    137         bool ok = true;
    138         for (size_t i = 0; ok && i < SK_ARRAY_COUNT(fIntArray); ++i) {
    139             if (fIntArray[i] != v) {
    140                 ok = false;
    141             }
    142         }
    143         return ok && this->INHERITED::checkValues(v);
    144     }
    145     virtual ~E() {}
    146 private:
    147     int   fIntArray[20];
    148 
    149     typedef A INHERITED;
    150 };
    151 
    152 A* A::Create(SkRandom* r) {
    153     switch (r->nextRangeU(0, 4)) {
    154         case 0:
    155             return new A;
    156         case 1:
    157             return new B;
    158         case 2:
    159             return new C;
    160         case 3:
    161             return new D;
    162         case 4:
    163             return new E;
    164         default:
    165             // suppress warning
    166             return nullptr;
    167     }
    168 }
    169 
    170 struct Rec {
    171     A* fInstance;
    172     int fValue;
    173 };
    174 
    175 DEF_TEST(GrMemoryPool, reporter) {
    176     // prealloc and min alloc sizes for the pool
    177     static const size_t gSizes[][2] = {
    178         {0, 0},
    179         {10 * sizeof(A), 20 * sizeof(A)},
    180         {100 * sizeof(A), 100 * sizeof(A)},
    181         {500 * sizeof(A), 500 * sizeof(A)},
    182         {10000 * sizeof(A), 0},
    183         {1, 100 * sizeof(A)},
    184     };
    185     // different percentages of creation vs deletion
    186     static const float gCreateFraction[] = {1.f, .95f, 0.75f, .5f};
    187     // number of create/destroys per test
    188     static const int kNumIters = 20000;
    189     // check that all the values stored in A objects are correct after this
    190     // number of iterations
    191     static const int kCheckPeriod = 500;
    192 
    193     SkRandom r;
    194     for (size_t s = 0; s < SK_ARRAY_COUNT(gSizes); ++s) {
    195         A::SetAllocator(gSizes[s][0], gSizes[s][1]);
    196         for (size_t c = 0; c < SK_ARRAY_COUNT(gCreateFraction); ++c) {
    197             SkTDArray<Rec> instanceRecs;
    198             for (int i = 0; i < kNumIters; ++i) {
    199                 float createOrDestroy = r.nextUScalar1();
    200                 if (createOrDestroy < gCreateFraction[c] ||
    201                     0 == instanceRecs.count()) {
    202                     Rec* rec = instanceRecs.append();
    203                     rec->fInstance = A::Create(&r);
    204                     rec->fValue = static_cast<int>(r.nextU());
    205                     rec->fInstance->setValues(rec->fValue);
    206                 } else {
    207                     int d = r.nextRangeU(0, instanceRecs.count() - 1);
    208                     Rec& rec = instanceRecs[d];
    209                     REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
    210                     delete rec.fInstance;
    211                     instanceRecs.removeShuffle(d);
    212                 }
    213                 if (0 == i % kCheckPeriod) {
    214                     for (int r = 0; r < instanceRecs.count(); ++r) {
    215                         Rec& rec = instanceRecs[r];
    216                         REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
    217                     }
    218                 }
    219             }
    220             for (int i = 0; i < instanceRecs.count(); ++i) {
    221                 Rec& rec = instanceRecs[i];
    222                 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
    223                 delete rec.fInstance;
    224             }
    225         }
    226     }
    227 }
    228 
    229 // GrMemoryPool requires that it's empty at the point of destruction. This helps
    230 // achieving that by releasing all added memory in the destructor.
    231 class AutoPoolReleaser {
    232 public:
    233     AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
    234     }
    235     ~AutoPoolReleaser() {
    236         for (void* ptr: fAllocated) {
    237             fPool.release(ptr);
    238         }
    239     }
    240     void add(void* ptr) {
    241         fAllocated.push_back(ptr);
    242     }
    243 private:
    244     GrMemoryPool& fPool;
    245     SkTArray<void*> fAllocated;
    246 };
    247 
    248 DEF_TEST(GrMemoryPoolAPI, reporter) {
    249     constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kSmallestMinAllocSize;
    250 
    251     // Allocates memory until pool adds a new block (pool.size() changes).
    252     auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) {
    253         size_t origPoolSize = pool.size();
    254         while (pool.size() == origPoolSize) {
    255             r.add(pool.allocate(31));
    256         }
    257     };
    258 
    259     // Effective prealloc space capacity is >= kSmallestMinAllocSize.
    260     {
    261         GrMemoryPool pool(0, 0);
    262         REPORTER_ASSERT(reporter, pool.preallocSize() == kSmallestMinAllocSize);
    263     }
    264 
    265     // Effective prealloc space capacity is >= minAllocSize.
    266     {
    267         constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
    268         GrMemoryPool pool(kSmallestMinAllocSize, kMinAllocSize);
    269         REPORTER_ASSERT(reporter, pool.preallocSize() == kMinAllocSize);
    270     }
    271 
    272     // Effective block size capacity >= kSmallestMinAllocSize.
    273     {
    274         GrMemoryPool pool(kSmallestMinAllocSize, kSmallestMinAllocSize / 2);
    275         AutoPoolReleaser r(pool);
    276 
    277         allocateMemory(pool, r);
    278         REPORTER_ASSERT(reporter, pool.size() == kSmallestMinAllocSize);
    279     }
    280 
    281     // Pool allocates exactly preallocSize on creation.
    282     {
    283         constexpr size_t kPreallocSize = kSmallestMinAllocSize * 5;
    284         GrMemoryPool pool(kPreallocSize, 0);
    285         REPORTER_ASSERT(reporter, pool.preallocSize() == kPreallocSize);
    286     }
    287 
    288     // Pool allocates exactly minAllocSize when it expands.
    289     {
    290         constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 7;
    291         GrMemoryPool pool(0, kMinAllocSize);
    292         AutoPoolReleaser r(pool);
    293 
    294         allocateMemory(pool, r);
    295         REPORTER_ASSERT(reporter, pool.size() == kMinAllocSize);
    296 
    297         allocateMemory(pool, r);
    298         REPORTER_ASSERT(reporter, pool.size() == 2 * kMinAllocSize);
    299     }
    300 
    301     // When asked to allocate amount > minAllocSize, pool allocates larger block
    302     // to accommodate all internal structures.
    303     {
    304         constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
    305         GrMemoryPool pool(kSmallestMinAllocSize, kMinAllocSize);
    306         AutoPoolReleaser r(pool);
    307 
    308         REPORTER_ASSERT(reporter, pool.size() == 0);
    309 
    310         constexpr size_t hugeSize = 10 * kMinAllocSize;
    311         r.add(pool.allocate(hugeSize));
    312         REPORTER_ASSERT(reporter, pool.size() > hugeSize);
    313 
    314         // Block size allocated to accommodate huge request doesn't include any extra
    315         // space, so next allocation request allocates a new block.
    316         size_t hugeBlockSize = pool.size();
    317         r.add(pool.allocate(0));
    318         REPORTER_ASSERT(reporter, pool.size() == hugeBlockSize + kMinAllocSize);
    319     }
    320 }
    321