Adds DynamicArena allocator that grows by fixed chunk size, fixes #2142.

Changes fixed size arenas to new dynamic arenas with reasonable chunk
sizes (may require tuning), refs #1842

This was SVN commit r13916.
This commit is contained in:
historic_bruno 2013-09-30 01:22:44 +00:00
parent fbee618ac8
commit e24ce51029
5 changed files with 104 additions and 21 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2010 Wildfire Games
/* Copyright (c) 2013 Wildfire Games
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -83,6 +83,91 @@ private:
LIB_API void TestArena();
/**
* allocator design parameters:
* - grow dynamically with a fixed chunkSize
* - for frequent allocations of size << chunkSize
* - no reallocations, pointers remain valid
**/
class DynamicArena
{
struct ArenaChunk
{
bool Available(size_t size) const
{
return size <= (capacity - end);
}
// Must check Available first or this may return an invalid address
uintptr_t Allocate(size_t size)
{
uintptr_t ptr = storage + end;
end += size;
return ptr;
}
uintptr_t storage;
size_t end;
size_t capacity;
ArenaChunk* next;
};
NONCOPYABLE(DynamicArena);
public:
DynamicArena(size_t chunkSize) : chunkSize(chunkSize), head(NULL)
{
AllocateNewChunk();
}
~DynamicArena()
{
ArenaChunk* chunk = head;
while (chunk != NULL)
{
ArenaChunk* next = chunk->next;
free(chunk);
chunk = next;
}
}
void AllocateNewChunk()
{
// For efficiency, do a single allocation with the ArenaChunk and its storage
ArenaChunk* next = head;
head = (ArenaChunk*)malloc(sizeof(ArenaChunk) + chunkSize);
ENSURE(head);
head->storage = sizeof(ArenaChunk) + uintptr_t(head);
head->end = 0;
head->capacity = chunkSize;
head->next = next;
}
void* allocate(size_t size)
{
if (size > chunkSize)
{
debug_warn(L"DynamicArena cannot allocate more than chunk size");
throw std::bad_alloc();
}
else if (!head->Available(size))
AllocateNewChunk();
return (void*)head->Allocate(size);
}
void deallocate(void* UNUSED(p), size_t UNUSED(size))
{
// ignored
}
private:
const size_t chunkSize;
ArenaChunk *head;
};
} // namespace Allocators
#endif // #ifndef INCLUDED_ALLOCATORS_ARENA

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2012 Wildfire Games.
/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -419,11 +419,11 @@ void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShade
* list in each, rebinding the GL state whenever it changes.
*/
Allocators::Arena<> arena(1*MiB);
typedef ProxyAllocator<void*, Allocators::Arena<> > ArenaProxyAllocator;
Allocators::DynamicArena arena(256 * KiB);
typedef ProxyAllocator<void*, Allocators::DynamicArena > ArenaProxyAllocator;
typedef std::vector<CModel*, ArenaProxyAllocator> ModelList_t;
typedef boost::unordered_map<SMRMaterialBucketKey, ModelList_t, SMRMaterialBucketKeyHash,
std::equal_to<SMRMaterialBucketKey>, ProxyAllocator<void*, Allocators::Arena<> >
std::equal_to<SMRMaterialBucketKey>, ProxyAllocator<void*, Allocators::DynamicArena >
> MaterialBuckets_t;
MaterialBuckets_t materialBuckets((MaterialBuckets_t::allocator_type(arena)));

View File

@ -684,12 +684,12 @@ void CPatchRData::Update(CSimulation2* simulation)
// std::map types with appropriate arena allocators and default comparison operator
#define POOLED_BATCH_MAP(Key, Value) \
std::map<Key, Value, std::less<Key>, ProxyAllocator<std::pair<Key const, Value>, Allocators::Arena<> > >
std::map<Key, Value, std::less<Key>, ProxyAllocator<std::pair<Key const, Value>, Allocators::DynamicArena > >
// Equivalent to "m[k]", when it returns a arena-allocated std::map (since we can't
// use the default constructor in that case)
template<typename M>
typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Allocators::Arena<>& arena)
typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
{
return m.insert(std::make_pair(k,
typename M::mapped_type(typename M::mapped_type::key_compare(), typename M::mapped_type::allocator_type(arena))
@ -698,7 +698,7 @@ typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Alloc
// Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
template<typename M>
typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Allocators::Arena<>& arena)
typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
{
return m.insert(std::make_pair(k, std::make_pair(
typename M::mapped_type::first_type(typename M::mapped_type::first_type::allocator_type(arena)),
@ -706,10 +706,8 @@ typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Allo
))).first->second;
}
static const size_t ARENA_SIZE = 4*MiB; // this should be enough for fairly huge maps
// Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
typedef std::pair<std::vector<GLint, ProxyAllocator<GLint, Allocators::Arena<> > >, std::vector<void*, ProxyAllocator<void*, Allocators::Arena<> > > > BatchElements;
typedef std::pair<std::vector<GLint, ProxyAllocator<GLint, Allocators::DynamicArena > >, std::vector<void*, ProxyAllocator<void*, Allocators::DynamicArena > > > BatchElements;
// Group batches by index buffer
typedef POOLED_BATCH_MAP(CVertexBuffer*, BatchElements) IndexBufferBatches;
@ -723,7 +721,7 @@ typedef POOLED_BATCH_MAP(CTerrainTextureEntry*, VertexBufferBatches) TextureBatc
void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
{
Allocators::Arena<> arena(ARENA_SIZE);
Allocators::DynamicArena arena(1 * MiB);
TextureBatches batches (TextureBatches::key_compare(), (TextureBatches::allocator_type(arena)));
@ -871,7 +869,7 @@ void CPatchRData::RenderBases(const std::vector<CPatchRData*>& patches, const CS
*/
struct SBlendBatch
{
SBlendBatch(Allocators::Arena<>& arena) :
SBlendBatch(Allocators::DynamicArena& arena) :
m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
{
}
@ -886,12 +884,12 @@ struct SBlendBatch
struct SBlendStackItem
{
SBlendStackItem(CVertexBuffer::VBChunk* v, CVertexBuffer::VBChunk* i,
const std::vector<CPatchRData::SSplat>& s, Allocators::Arena<>& arena) :
const std::vector<CPatchRData::SSplat>& s, Allocators::DynamicArena& arena) :
vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(arena))
{
}
typedef std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Allocators::Arena<> > > SplatStack;
typedef std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Allocators::DynamicArena > > SplatStack;
CVertexBuffer::VBChunk* vertices;
CVertexBuffer::VBChunk* indices;
SplatStack splats;
@ -900,9 +898,9 @@ struct SBlendStackItem
void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches, const CShaderDefines& context,
ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
{
Allocators::Arena<> arena(ARENA_SIZE);
Allocators::DynamicArena arena(1 * MiB);
typedef std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Allocators::Arena<> > > BatchesStack;
typedef std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Allocators::DynamicArena > > BatchesStack;
BatchesStack batches((BatchesStack::allocator_type(arena)));
CShaderDefines contextBlend = context;
@ -914,7 +912,7 @@ void CPatchRData::RenderBlends(const std::vector<CPatchRData*>& patches, const C
// to avoid heavy reallocations
batches.reserve(256);
typedef std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Allocators::Arena<> > > BlendStacks;
typedef std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Allocators::DynamicArena > > BlendStacks;
BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
blendStacks.reserve(patches.size());

View File

@ -57,7 +57,7 @@ static u8 GetArrayType(uint32 arrayType)
CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(ScriptInterface& scriptInterface, ISerializer& serializer) :
m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_Rooter(m_ScriptInterface),
m_ScriptBackrefsArena(16*MiB), m_ScriptBackrefs(backrefs_t::key_compare(), ScriptBackrefsAlloc(m_ScriptBackrefsArena)), m_ScriptBackrefsNext(1)
m_ScriptBackrefsArena(1 * MiB), m_ScriptBackrefs(backrefs_t::key_compare(), ScriptBackrefsAlloc(m_ScriptBackrefsArena)), m_ScriptBackrefsNext(1)
{
}

View File

@ -65,10 +65,10 @@ private:
ISerializer& m_Serializer;
// Pooling helps since we do a lot of short-lived allocations
typedef ProxyAllocator<std::pair<JSObject* const, u32>, Allocators::Arena<> > ScriptBackrefsAlloc;
typedef ProxyAllocator<std::pair<JSObject* const, u32>, Allocators::DynamicArena> ScriptBackrefsAlloc;
typedef std::map<JSObject*, u32, std::less<JSObject*>, ScriptBackrefsAlloc> backrefs_t;
Allocators::Arena<> m_ScriptBackrefsArena;
Allocators::DynamicArena m_ScriptBackrefsArena;
backrefs_t m_ScriptBackrefs;
u32 m_ScriptBackrefsNext;
u32 GetScriptBackrefTag(JSObject* obj);