From e24ce510294ef1128376740e18d42716daa18ff0 Mon Sep 17 00:00:00 2001 From: historic_bruno Date: Mon, 30 Sep 2013 01:22:44 +0000 Subject: [PATCH] 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. --- source/lib/allocators/arena.h | 87 ++++++++++++++++++- source/renderer/ModelRenderer.cpp | 8 +- source/renderer/PatchRData.cpp | 24 +++-- .../serialization/BinarySerializer.cpp | 2 +- .../serialization/BinarySerializer.h | 4 +- 5 files changed, 104 insertions(+), 21 deletions(-) diff --git a/source/lib/allocators/arena.h b/source/lib/allocators/arena.h index 82650880ca..a834d7b658 100644 --- a/source/lib/allocators/arena.h +++ b/source/lib/allocators/arena.h @@ -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 diff --git a/source/renderer/ModelRenderer.cpp b/source/renderer/ModelRenderer.cpp index 2579497a30..1cfaa43774 100644 --- a/source/renderer/ModelRenderer.cpp +++ b/source/renderer/ModelRenderer.cpp @@ -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 > ArenaProxyAllocator; + Allocators::DynamicArena arena(256 * KiB); + typedef ProxyAllocator ArenaProxyAllocator; typedef std::vector ModelList_t; typedef boost::unordered_map, ProxyAllocator > + std::equal_to, ProxyAllocator > MaterialBuckets_t; MaterialBuckets_t materialBuckets((MaterialBuckets_t::allocator_type(arena))); diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index e623bdf075..8cdf2657fc 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -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, ProxyAllocator, Allocators::Arena<> > > + std::map, ProxyAllocator, 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::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::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 > > > BatchElements; +typedef std::pair >, std::vector > > 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& 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& 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& s, Allocators::Arena<>& arena) : + const std::vector& s, Allocators::DynamicArena& arena) : vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(arena)) { } - typedef std::vector > > SplatStack; + typedef std::vector > SplatStack; CVertexBuffer::VBChunk* vertices; CVertexBuffer::VBChunk* indices; SplatStack splats; @@ -900,9 +898,9 @@ struct SBlendStackItem void CPatchRData::RenderBlends(const std::vector& patches, const CShaderDefines& context, ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy) { - Allocators::Arena<> arena(ARENA_SIZE); + Allocators::DynamicArena arena(1 * MiB); - typedef std::vector > > BatchesStack; + typedef std::vector > BatchesStack; BatchesStack batches((BatchesStack::allocator_type(arena))); CShaderDefines contextBlend = context; @@ -914,7 +912,7 @@ void CPatchRData::RenderBlends(const std::vector& patches, const C // to avoid heavy reallocations batches.reserve(256); - typedef std::vector > > BlendStacks; + typedef std::vector > BlendStacks; BlendStacks blendStacks((BlendStacks::allocator_type(arena))); blendStacks.reserve(patches.size()); diff --git a/source/simulation2/serialization/BinarySerializer.cpp b/source/simulation2/serialization/BinarySerializer.cpp index 17f51da134..64e8622547 100644 --- a/source/simulation2/serialization/BinarySerializer.cpp +++ b/source/simulation2/serialization/BinarySerializer.cpp @@ -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) { } diff --git a/source/simulation2/serialization/BinarySerializer.h b/source/simulation2/serialization/BinarySerializer.h index d79d44b6a1..9953995a64 100644 --- a/source/simulation2/serialization/BinarySerializer.h +++ b/source/simulation2/serialization/BinarySerializer.h @@ -65,10 +65,10 @@ private: ISerializer& m_Serializer; // Pooling helps since we do a lot of short-lived allocations - typedef ProxyAllocator, Allocators::Arena<> > ScriptBackrefsAlloc; + typedef ProxyAllocator, Allocators::DynamicArena> ScriptBackrefsAlloc; typedef std::map, ScriptBackrefsAlloc> backrefs_t; - Allocators::Arena<> m_ScriptBackrefsArena; + Allocators::DynamicArena m_ScriptBackrefsArena; backrefs_t m_ScriptBackrefs; u32 m_ScriptBackrefsNext; u32 GetScriptBackrefTag(JSObject* obj);