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); 23 } 24 virtual bool checkValues(int v) { 25 return fChar == static_cast<char>(v); 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