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:
parent
ff793db2ef
commit
ebd24962df
61
bench/MemoryBench.cpp
Normal file
61
bench/MemoryBench.cpp
Normal 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);
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user