Add GrMemoryPool as a helper to override operators new/delete

Review URL: http://codereview.appspot.com/6306090/



git-svn-id: http://skia.googlecode.com/svn/trunk@4282 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2012-06-19 15:40:27 +00:00
Родитель 50e4ce0552
Коммит 4da34e36cb
9 изменённых файлов: 652 добавлений и 3 удалений

164
bench/GrMemoryPoolBench.cpp Normal file
Просмотреть файл

@ -0,0 +1,164 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrMemoryPool.h"
#include "SkBenchmark.h"
#include "SkRandom.h"
#include "SkTScopedPtr.h"
#include "SkTDArray.h"
// change this to 0 to compare GrMemoryPool to default new / delete
#define OVERRIDE_NEW 1
namespace {
struct A {
int gStuff[10];
#if OVERRIDE_NEW
void* operator new (size_t size) { return gPool.allocate(size); }
void operator delete (void* mem) { if (mem) { return gPool.release(mem); } }
#endif
static GrMemoryPool gPool;
};
GrMemoryPool A::gPool(10 * (1 << 10), 10 * (1 << 10));
}
/**
* This benchmark creates and deletes objects in stack order
*/
class GrMemoryPoolBenchStack : public SkBenchmark {
enum {
N = SkBENCHLOOP(5 * (1 << 20)),
};
public:
GrMemoryPoolBenchStack(void* param) : INHERITED(param) {
}
protected:
virtual const char* onGetName() {
return "grmemorypool_stack";
}
virtual void onDraw(SkCanvas* canvas) {
SkRandom r;
enum {
kMaxObjects = 4 * (1 << 10),
};
A* objects[kMaxObjects];
// We delete if a random [-1, 1] fixed pt is < the thresh. Otherwise,
// we allocate. We start allocate-biased and ping-pong to delete-biased
SkFixed delThresh = -SK_FixedHalf;
enum {
kSwitchThreshPeriod = N / (2 * kMaxObjects),
};
int s = 0;
int count = 0;
for (int i = 0; i < N; i++, ++s) {
if (kSwitchThreshPeriod == s) {
delThresh = -delThresh;
s = 0;
}
SkFixed del = r.nextSFixed1();
if (count &&
(kMaxObjects == count || del < delThresh)) {
delete objects[count-1];
--count;
} else {
objects[count] = new A;
++count;
}
}
for (int i = 0; i < count; ++i) {
delete objects[i];
}
}
private:
typedef SkBenchmark INHERITED;
};
/**
* This benchmark creates objects and deletes them in random order
*/
class GrMemoryPoolBenchRandom : public SkBenchmark {
enum {
N = SkBENCHLOOP(5 * (1 << 20)),
};
public:
GrMemoryPoolBenchRandom(void* param) : INHERITED(param) {
}
protected:
virtual const char* onGetName() {
return "grmemorypool_random";
}
virtual void onDraw(SkCanvas* canvas) {
SkRandom r;
enum {
kMaxObjects = 4 * (1 << 10),
};
SkTScopedPtr<A> objects[kMaxObjects];
for (int i = 0; i < N; i++) {
uint32_t idx = r.nextRangeU(0, kMaxObjects-1);
if (NULL == objects[idx].get()) {
objects[idx].reset(new A);
} else {
objects[idx].reset(NULL);
}
}
}
private:
typedef SkBenchmark INHERITED;
};
/**
* This benchmark creates objects and deletes them in queue order
*/
class GrMemoryPoolBenchQueue : public SkBenchmark {
enum {
N = SkBENCHLOOP((1 << 10)),
M = SkBENCHLOOP(4 * (1 << 10)),
};
public:
GrMemoryPoolBenchQueue(void* param) : INHERITED(param) {
}
protected:
virtual const char* onGetName() {
return "grmemorypool_queue";
}
virtual void onDraw(SkCanvas* canvas) {
SkRandom r;
A* objects[M];
for (int i = 0; i < N; i++) {
uint32_t count = r.nextRangeU(0, M-1);
for (uint32_t i = 0; i < count; i++) {
objects[i] = new A;
}
for (uint32_t i = 0; i < count; i++) {
delete objects[i];
}
}
}
private:
typedef SkBenchmark INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
static SkBenchmark* Fact1(void* p) { return new GrMemoryPoolBenchStack(p); }
static SkBenchmark* Fact2(void* p) { return new GrMemoryPoolBenchRandom(p); }
static SkBenchmark* Fact3(void* p) { return new GrMemoryPoolBenchQueue(p); }
static BenchRegistry gReg01(Fact1);
static BenchRegistry gReg02(Fact2);
static BenchRegistry gReg03(Fact3);

Просмотреть файл

@ -25,6 +25,7 @@
'../bench/DecodeBench.cpp',
'../bench/FontScalerBench.cpp',
'../bench/GradientBench.cpp',
'../bench/GrMemoryPoolBench.cpp',
'../bench/InterpBench.cpp',
'../bench/MathBench.cpp',
'../bench/MatrixBench.cpp',

Просмотреть файл

@ -243,6 +243,8 @@
'../src/gpu/GrInOrderDrawBuffer.h',
'../src/gpu/GrMatrix.cpp',
'../src/gpu/GrMemory.cpp',
'../src/gpu/GrMemoryPool.cpp',
'../src/gpu/GrMemoryPool.h',
'../src/gpu/GrPath.h',
'../src/gpu/GrPathRendererChain.cpp',
'../src/gpu/GrPathRendererChain.h',

Просмотреть файл

@ -44,6 +44,7 @@
'../tests/GLInterfaceValidation.cpp',
'../tests/GLProgramsTest.cpp',
'../tests/GradientTest.cpp',
'../tests/GrMemoryPoolTest.cpp',
'../tests/InfRectTest.cpp',
'../tests/MathTest.cpp',
'../tests/MatrixTest.cpp',

Просмотреть файл

@ -53,6 +53,10 @@
static SkTArray<PFCheckInstCnt> gChildren; \
} fInstanceCountHelper; \
\
static int32_t GetInstanceCount() { \
return SkInstanceCountHelper::gInstanceCount; \
} \
\
static void CheckInstanceCount() { \
if (0 != SkInstanceCountHelper::gInstanceCount) { \
SkDebugf("Leaked %s objects: %d\n", #className, \

Просмотреть файл

@ -79,27 +79,33 @@ static inline int32_t GrIDivRoundUp(int x, int y) {
static inline uint32_t GrUIDivRoundUp(uint32_t x, uint32_t y) {
return (x + (y-1)) / y;
}
static inline size_t GrSizeDivRoundUp(size_t x, uint32_t y) {
static inline size_t GrSizeDivRoundUp(size_t x, size_t y) {
return (x + (y-1)) / y;
}
// compile time, evaluates Y multiple times
#define GR_CT_DIV_ROUND_UP(X, Y) (((X) + ((Y)-1)) / (Y))
/**
* align up
*/
static inline uint32_t GrUIAlignUp(uint32_t x, uint32_t alignment) {
return GrUIDivRoundUp(x, alignment) * alignment;
}
static inline uint32_t GrSizeAlignUp(size_t x, uint32_t alignment) {
static inline size_t GrSizeAlignUp(size_t x, size_t alignment) {
return GrSizeDivRoundUp(x, alignment) * alignment;
}
// compile time, evaluates A multiple times
#define GR_CT_ALIGN_UP(X, A) (GR_CT_DIV_ROUND_UP((X),(A)) * (A))
/**
* amount of pad needed to align up
*/
static inline uint32_t GrUIAlignUpPad(uint32_t x, uint32_t alignment) {
return (alignment - x % alignment) % alignment;
}
static inline size_t GrSizeAlignUpPad(size_t x, uint32_t alignment) {
static inline size_t GrSizeAlignUpPad(size_t x, size_t alignment) {
return (alignment - x % alignment) % alignment;
}

153
src/gpu/GrMemoryPool.cpp Normal file
Просмотреть файл

@ -0,0 +1,153 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrMemoryPool.h"
#if GR_DEBUG
#define VALIDATE this->validate()
#else
#define VALIDATE
#endif
GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) {
GR_DEBUGCODE(fAllocationCnt = 0);
minAllocSize = GrMax<size_t>(minAllocSize, 1 << 10);
fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment),
fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment);
fPreallocSize = GrMax(fPreallocSize, fMinAllocSize);
fHead = CreateBlock(fPreallocSize);
fTail = fHead;
fHead->fNext = NULL;
fHead->fPrev = NULL;
VALIDATE;
};
GrMemoryPool::~GrMemoryPool() {
VALIDATE;
GrAssert(0 == fAllocationCnt);
GrAssert(fHead == fTail);
GrAssert(0 == fHead->fLiveCount);
DeleteBlock(fHead);
};
void* GrMemoryPool::allocate(size_t size) {
VALIDATE;
size = GrSizeAlignUp(size, kAlignment);
size += kPerAllocPad;
if (fTail->fFreeSize < size) {
int blockSize = size;
blockSize = GrMax<size_t>(blockSize, fMinAllocSize);
BlockHeader* block = CreateBlock(blockSize);
block->fPrev = fTail;
block->fNext = NULL;
GrAssert(NULL == fTail->fNext);
fTail->fNext = block;
fTail = block;
}
GrAssert(fTail->fFreeSize >= size);
intptr_t ptr = fTail->fCurrPtr;
// We stash a pointer to the block header, just before the allocated space,
// so that we can decrement the live count on delete in constant time.
*reinterpret_cast<BlockHeader**>(ptr) = fTail;
ptr += kPerAllocPad;
fTail->fCurrPtr += size;
fTail->fFreeSize -= size;
fTail->fLiveCount += 1;
GR_DEBUGCODE(++fAllocationCnt);
VALIDATE;
return reinterpret_cast<void*>(ptr);
}
void GrMemoryPool::release(void* p) {
VALIDATE;
intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad;
BlockHeader* block = *reinterpret_cast<BlockHeader**>(ptr);
if (1 == block->fLiveCount) {
// the head block is special, it is reset rather than deleted
if (fHead == block) {
fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) +
kHeaderSize;
fHead->fLiveCount = 0;
fHead->fFreeSize = fPreallocSize;
} else {
BlockHeader* prev = block->fPrev;
BlockHeader* next = block->fNext;
GrAssert(prev);
prev->fNext = next;
if (next) {
next->fPrev = prev;
} else {
GrAssert(fTail == block);
fTail = prev;
}
DeleteBlock(block);
}
} else {
--block->fLiveCount;
}
GR_DEBUGCODE(--fAllocationCnt);
VALIDATE;
}
GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) {
BlockHeader* block =
reinterpret_cast<BlockHeader*>(GrMalloc(size + kHeaderSize));
// we assume malloc gives us aligned memory
GrAssert(!(reinterpret_cast<intptr_t>(block) % kAlignment));
block->fLiveCount = 0;
block->fFreeSize = size;
block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize;
return block;
}
void GrMemoryPool::DeleteBlock(BlockHeader* block) {
GrFree(block);
}
void GrMemoryPool::validate() {
BlockHeader* block = fHead;
BlockHeader* prev = NULL;
GrAssert(block);
int allocCount = 0;
do {
allocCount += block->fLiveCount;
GrAssert(prev == block->fPrev);
if (NULL != prev) {
GrAssert(prev->fNext == block);
}
intptr_t b = reinterpret_cast<intptr_t>(block);
size_t ptrOffset = block->fCurrPtr - b;
size_t totalSize = ptrOffset + block->fFreeSize;
size_t userSize = totalSize - kHeaderSize;
intptr_t userStart = b + kHeaderSize;
GrAssert(!(b % kAlignment));
GrAssert(!(totalSize % kAlignment));
GrAssert(!(userSize % kAlignment));
GrAssert(!(block->fCurrPtr % kAlignment));
if (fHead != block) {
GrAssert(block->fLiveCount);
GrAssert(userSize >= fMinAllocSize);
} else {
GrAssert(userSize == fPreallocSize);
}
if (!block->fLiveCount) {
GrAssert(ptrOffset == kHeaderSize);
GrAssert(userStart == block->fCurrPtr);
} else {
GrAssert(block == *reinterpret_cast<BlockHeader**>(userStart));
}
prev = block;
} while ((block = block->fNext));
GrAssert(allocCount == fAllocationCnt);
GrAssert(prev == fTail);
}

79
src/gpu/GrMemoryPool.h Normal file
Просмотреть файл

@ -0,0 +1,79 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrMemoryPool_DEFINED
#define GrMemoryPool_DEFINED
#include "GrTypes.h"
/**
* Allocates memory in blocks and parcels out space in the blocks for allocation
* requests. It is optimized for allocate / release speed over memory
* effeciency. The interface is designed to be used to implement operator new
* and delete overrides. All allocations are expected to be released before the
* pool's destructor is called. Allocations will be 8-byte aligned.
*/
class GrMemoryPool {
public:
/**
* Prealloc size is the amount of space to make available at pool creation
* time and keep around until pool destruction. The min alloc size is the
* smallest allowed size of additional allocations.
*/
GrMemoryPool(size_t preallocSize, size_t minAllocSize);
~GrMemoryPool();
/**
* Allocates memory. The memory must be freed with release().
*/
void* allocate(size_t size);
/**
* p must have been returned by allocate()
*/
void release(void* p);
/**
* Returns true if there are no unreleased allocations.
*/
bool isEmpty() const { return fTail == fHead && !fHead->fLiveCount; }
private:
struct BlockHeader;
BlockHeader* CreateBlock(size_t size);
void DeleteBlock(BlockHeader* block);
void validate();
struct BlockHeader {
BlockHeader* fNext; // doubly-linked list of blocks.
BlockHeader* fPrev;
int fLiveCount; // number of outstanding allocations in the
// block.
intptr_t fCurrPtr; // ptr to the start of blocks free space.
size_t fFreeSize; // amount of free space left in the block.
};
enum {
// We assume this alignment is good enough for everybody.
kAlignment = 8,
kHeaderSize = GR_CT_ALIGN_UP(sizeof(BlockHeader), kAlignment),
kPerAllocPad = GR_CT_ALIGN_UP(sizeof(BlockHeader*), kAlignment),
};
size_t fPreallocSize;
size_t fMinAllocSize;
BlockHeader* fHead;
BlockHeader* fTail;
#if GR_DEBUG
int fAllocationCnt;
#endif
};
#endif

239
tests/GrMemoryPoolTest.cpp Normal file
Просмотреть файл

@ -0,0 +1,239 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Test.h"
#include "GrMemoryPool.h"
#include "SkRandom.h"
#include "SkTDArray.h"
#include "SkTScopedPtr.h"
#include "SkInstCnt.h"
namespace {
// A is the top of an inheritance tree of classes that overload op new and
// and delete to use a GrMemoryPool. The objects have values of different types
// that can be set and checked.
class A {
public:
A() {};
virtual void setValues(int v) {
fChar = static_cast<char>(v);
}
virtual bool checkValues(int v) {
return fChar == static_cast<char>(v);
}
virtual ~A() {};
void* operator new(size_t size) {
if (!gPool.get()) {
return ::operator new(size);
} else {
return gPool->allocate(size);
}
}
void operator delete(void* p) {
if (!gPool.get()) {
::operator delete(p);
} else {
return gPool->release(p);
}
}
SK_DECLARE_INST_COUNT_ROOT(A);
static A* Create(SkRandom* r);
static void SetAllocator(size_t preallocSize, size_t minAllocSize) {
SkASSERT(0 == GetInstanceCount());
GrMemoryPool* pool = new GrMemoryPool(preallocSize, minAllocSize);
gPool.reset(pool);
}
static void ResetAllocator() {
SkASSERT(0 == GetInstanceCount());
gPool.reset(NULL);
}
private:
static SkTScopedPtr<GrMemoryPool> gPool;
char fChar;
};
SK_DEFINE_INST_COUNT(A);
SkTScopedPtr<GrMemoryPool> A::gPool;
class B : public A {
public:
B() {};
virtual void setValues(int v) {
fDouble = static_cast<double>(v);
this->INHERITED::setValues(v);
}
virtual bool checkValues(int v) {
return fDouble == static_cast<double>(v) &&
this->INHERITED::checkValues(v);
}
virtual ~B() {};
private:
double fDouble;
typedef A INHERITED;
};
class C : public A {
public:
C() {};
virtual void setValues(int v) {
fInt64 = static_cast<int64_t>(v);
this->INHERITED::setValues(v);
}
virtual bool checkValues(int v) {
return fInt64 == static_cast<int64_t>(v) &&
this->INHERITED::checkValues(v);
}
virtual ~C() {};
private:
int64_t fInt64;
typedef A INHERITED;
};
// D derives from C and owns a dynamically created B
class D : public C {
public:
D() {
fB = new B();
}
virtual void setValues(int v) {
fVoidStar = reinterpret_cast<void*>(v);
this->INHERITED::setValues(v);
fB->setValues(v);
}
virtual bool checkValues(int v) {
return fVoidStar == reinterpret_cast<void*>(v) &&
fB->checkValues(v) &&
this->INHERITED::checkValues(v);
}
virtual ~D() {
delete fB;
}
private:
void* fVoidStar;
B* fB;
typedef C INHERITED;
};
class E : public A {
public:
E() {}
virtual void setValues(int v) {
for (size_t i = 0; i < SK_ARRAY_COUNT(fIntArray); ++i) {
fIntArray[i] = v;
}
this->INHERITED::setValues(v);
}
virtual bool checkValues(int v) {
bool ok = true;
for (size_t i = 0; ok && i < SK_ARRAY_COUNT(fIntArray); ++i) {
if (fIntArray[i] != v) {
ok = false;
}
}
return ok && this->INHERITED::checkValues(v);
}
virtual ~E() {}
private:
int fIntArray[20];
typedef A INHERITED;
};
A* A::Create(SkRandom* r) {
switch (r->nextRangeU(0, 4)) {
case 0:
return new A;
case 1:
return new B;
case 2:
return new C;
case 3:
return new D;
case 4:
return new E;
default:
// suppress warning
return NULL;
}
}
}
struct Rec {
A* fInstance;
int fValue;
};
static void test_memory_pool(skiatest::Reporter* reporter) {
// prealloc and min alloc sizes for the pool
static const size_t gSizes[][2] = {
{0, 0},
{10 * sizeof(A), 20 * sizeof(A)},
{100 * sizeof(A), 100 * sizeof(A)},
{500 * sizeof(A), 500 * sizeof(A)},
{10000 * sizeof(A), 0},
{1, 100 * sizeof(A)},
};
// different percentages of creation vs deletion
static const float gCreateFraction[] = {1.f, .95f, 0.75f, .5f};
// number of create/destroys per test
static const int kNumIters = 20000;
// check that all the values stored in A objects are correct after this
// number of iterations
static const int kCheckPeriod = 500;
SkRandom r;
for (size_t s = 0; s < SK_ARRAY_COUNT(gSizes); ++s) {
A::SetAllocator(gSizes[s][0], gSizes[s][1]);
for (size_t c = 0; c < SK_ARRAY_COUNT(gCreateFraction); ++c) {
SkTDArray<Rec> instanceRecs;
for (int i = 0; i < kNumIters; ++i) {
float createOrDestroy = r.nextUScalar1();
if (createOrDestroy < gCreateFraction[c] ||
0 == instanceRecs.count()) {
Rec* rec = instanceRecs.append();
rec->fInstance = A::Create(&r);
rec->fValue = static_cast<int>(r.nextU());
rec->fInstance->setValues(rec->fValue);
} else {
int d = r.nextRangeU(0, instanceRecs.count() - 1);
Rec& rec = instanceRecs[d];
REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
delete rec.fInstance;
instanceRecs.removeShuffle(d);
}
if (0 == i % kCheckPeriod) {
for (int r = 0; r < instanceRecs.count(); ++r) {
Rec& rec = instanceRecs[r];
REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
}
}
}
for (int i = 0; i < instanceRecs.count(); ++i) {
Rec& rec = instanceRecs[i];
REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
delete rec.fInstance;
}
#ifdef SK_DEBUG
REPORTER_ASSERT(reporter, !A::GetInstanceCount());
#endif
}
}
}
#include "TestClassDef.h"
DEFINE_TESTCLASS("GrMemoryPool", GrMemoryPoolClass, test_memory_pool)