change SkChunkAlloc to grow its allocations geometrically (not linearly)

plus add a bench and unittest for it.



git-svn-id: http://skia.googlecode.com/svn/trunk@3989 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2012-05-17 14:28:11 +00:00
parent ff793db2ef
commit ebd24962df
5 changed files with 134 additions and 57 deletions

61
bench/MemoryBench.cpp Normal file
View File

@ -0,0 +1,61 @@
/*
* 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 "SkBenchmark.h"
#include "SkCanvas.h"
#include "SkPaint.h"
#include "SkRandom.h"
#include "SkChunkAlloc.h"
#include "SkString.h"
class ChunkAllocBench : public SkBenchmark {
SkString fName;
size_t fMinSize;
enum {
N = SkBENCHLOOP(1000)
};
public:
ChunkAllocBench(void* param, size_t minSize) : INHERITED(param) {
fMinSize = minSize;
fName.printf("chunkalloc_%d", minSize);
}
protected:
virtual const char* onGetName() SK_OVERRIDE {
return fName.c_str();
}
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
size_t inc = fMinSize >> 4;
SkASSERT(inc > 0);
size_t total = fMinSize * 64;
SkChunkAlloc alloc(fMinSize);
for (int i = 0; i < N; ++i) {
size_t size = 0;
int calls = 0;
while (size < total) {
alloc.allocThrow(inc);
size += inc;
calls += 1;
}
alloc.reset();
}
}
private:
typedef SkBenchmark INHERITED;
};
static SkBenchmark* F0(void* p) { return new ChunkAllocBench(p, 64); }
static SkBenchmark* F1(void* p) { return new ChunkAllocBench(p, 8*1024); }
static BenchRegistry gR0(F0);
static BenchRegistry gR1(F1);

View File

@ -28,6 +28,7 @@
'../bench/InterpBench.cpp',
'../bench/MathBench.cpp',
'../bench/MatrixBench.cpp',
'../bench/MemoryBench.cpp',
'../bench/MutexBench.cpp',
'../bench/PathBench.cpp',
'../bench/PathIterBench.cpp',

View File

@ -17,18 +17,12 @@ public:
SkChunkAlloc(size_t minSize);
~SkChunkAlloc();
/** Free up all allocated blocks. This invalidates all returned
pointers.
*/
/**
* Free up all allocated blocks. This invalidates all returned
* pointers.
*/
void reset();
/** Reuse all allocated blocks. This invalidates all returned
pointers (like reset) but doesn't necessarily free up all
of the privately allocated blocks. This is more efficient
if you plan to reuse the allocator multiple times.
*/
void reuse();
enum AllocFailType {
kReturnNil_AllocFailType,
kThrow_AllocFailType
@ -48,6 +42,7 @@ public:
size_t unalloc(void* ptr);
size_t totalCapacity() const { return fTotalCapacity; }
int blockCount() const { return fBlockCount; }
/**
* Returns true if the specified address is within one of the chunks, and
@ -58,10 +53,12 @@ public:
private:
struct Block;
Block* fBlock;
size_t fMinSize;
Block* fPool;
size_t fChunkSize;
size_t fTotalCapacity;
int fBlockCount;
Block* newBlock(size_t bytes, AllocFailType ftype);
};

View File

@ -1,4 +1,3 @@
/*
* Copyright 2006 The Android Open Source Project
*
@ -6,9 +5,18 @@
* found in the LICENSE file.
*/
#include "SkChunkAlloc.h"
// Don't malloc any chunks smaller than this
#define MIN_CHUNKALLOC_BLOCK_SIZE 1024
// Return the new min blocksize given the current value
static size_t increase_next_size(size_t size) {
return size + (size >> 1);
}
///////////////////////////////////////////////////////////////////////////////
struct SkChunkAlloc::Block {
Block* fNext;
size_t fFreeSize;
@ -19,8 +27,7 @@ struct SkChunkAlloc::Block {
return reinterpret_cast<char*>(this + 1);
}
void freeChain() { // this can be null
Block* block = this;
static void FreeChain(Block* block) {
while (block) {
Block* next = block->fNext;
sk_free(block);
@ -28,29 +35,24 @@ struct SkChunkAlloc::Block {
}
};
Block* tail() {
Block* block = this;
if (block) {
for (;;) {
Block* next = block->fNext;
if (NULL == next) {
break;
}
block = next;
}
}
return block;
}
bool contains(const void* addr) const {
const char* ptr = reinterpret_cast<const char*>(addr);
return ptr >= (const char*)(this + 1) && ptr < fFreePtr;
}
};
SkChunkAlloc::SkChunkAlloc(size_t minSize)
: fBlock(NULL), fMinSize(SkAlign4(minSize)), fPool(NULL), fTotalCapacity(0)
{
///////////////////////////////////////////////////////////////////////////////
SkChunkAlloc::SkChunkAlloc(size_t minSize) {
if (minSize < MIN_CHUNKALLOC_BLOCK_SIZE) {
minSize = MIN_CHUNKALLOC_BLOCK_SIZE;
}
fBlock = NULL;
fMinSize = minSize;
fChunkSize = fMinSize;
fTotalCapacity = 0;
fBlockCount = 0;
}
SkChunkAlloc::~SkChunkAlloc() {
@ -58,35 +60,20 @@ SkChunkAlloc::~SkChunkAlloc() {
}
void SkChunkAlloc::reset() {
fBlock->freeChain();
fBlock = NULL;
fPool->freeChain();
fPool = NULL;
fTotalCapacity = 0;
}
void SkChunkAlloc::reuse() {
if (fPool && fBlock) {
fPool->tail()->fNext = fBlock;
}
fPool = fBlock;
Block::FreeChain(fBlock);
fBlock = NULL;
fChunkSize = fMinSize; // reset to our initial minSize
fTotalCapacity = 0;
fBlockCount = 0;
}
SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) {
Block* block = fPool;
if (block && bytes <= block->fFreeSize) {
fPool = block->fNext;
return block;
size_t size = bytes;
if (size < fChunkSize) {
size = fChunkSize;
}
size_t size = bytes;
if (size < fMinSize)
size = fMinSize;
block = (Block*)sk_malloc_flags(sizeof(Block) + size,
Block* block = (Block*)sk_malloc_flags(sizeof(Block) + size,
ftype == kThrow_AllocFailType ? SK_MALLOC_THROW : 0);
if (block) {
@ -95,6 +82,9 @@ SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) {
block->fFreePtr = block->startOfData();
fTotalCapacity += size;
fBlockCount += 1;
fChunkSize = increase_next_size(fChunkSize);
}
return block;
}
@ -114,10 +104,10 @@ void* SkChunkAlloc::alloc(size_t bytes, AllocFailType ftype) {
}
SkASSERT(block && bytes <= block->fFreeSize);
void* ptr = block->fFreePtr;
char* ptr = block->fFreePtr;
block->fFreeSize -= bytes;
block->fFreePtr += bytes;
block->fFreePtr = ptr + bytes;
return ptr;
}

View File

@ -1,13 +1,39 @@
/*
* 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 "SkChunkAlloc.h"
#include "SkUtils.h"
static void test_chunkalloc(skiatest::Reporter* reporter) {
size_t min = 256;
SkChunkAlloc alloc(min);
REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity());
REPORTER_ASSERT(reporter, 0 == alloc.blockCount());
REPORTER_ASSERT(reporter, !alloc.contains(NULL));
REPORTER_ASSERT(reporter, !alloc.contains(reporter));
alloc.reset();
REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity());
REPORTER_ASSERT(reporter, 0 == alloc.blockCount());
size_t size = min >> 1;
void* ptr = alloc.allocThrow(size);
REPORTER_ASSERT(reporter, alloc.totalCapacity() >= size);
REPORTER_ASSERT(reporter, alloc.blockCount() > 0);
REPORTER_ASSERT(reporter, alloc.contains(ptr));
alloc.reset();
REPORTER_ASSERT(reporter, !alloc.contains(ptr));
}
///////////////////////////////////////////////////////////////////////////////
static void set_zero(void* dst, size_t bytes) {
char* ptr = (char*)dst;
for (size_t i = 0; i < bytes; ++i) {
@ -85,6 +111,8 @@ static void test_32(skiatest::Reporter* reporter) {
static void TestMemset(skiatest::Reporter* reporter) {
test_16(reporter);
test_32(reporter);
test_chunkalloc(reporter);
};
#include "TestClassDef.h"