1 /****************************************************************************
2 * Copyright (C) 2014-2015 Intel Corporation. All Rights Reserved.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * @brief Arena memory manager
26 * The arena is convenient and fast for managing allocations for any of
27 * our allocations that are associated with operations and can all be freed
28 * once when their operation has completed. Allocations are cheap since
29 * most of the time its simply an increment of an offset. Also, no need to
30 * free individual allocations. All of the arena memory can be freed at once.
32 ******************************************************************************/
38 #include "core/utils.h"
40 class DefaultAllocator
43 void* AllocateAligned(size_t size
, size_t align
)
45 void* p
= _aligned_malloc(size
, align
);
54 static const size_t ARENA_BLOCK_ALIGN
= KNOB_SIMD_WIDTH
* 4;
60 ArenaBlock
* pNext
= nullptr;
62 static_assert(sizeof(ArenaBlock
) <= ARENA_BLOCK_ALIGN
, "Increase BLOCK_ALIGN size");
64 template<typename MutexT
= std::mutex
, typename T
= DefaultAllocator
>
68 TArena(T
& in_allocator
) : m_allocator(in_allocator
) {}
69 TArena() : m_allocator(m_defAllocator
) {}
75 void* AllocAligned(size_t size
, size_t align
)
79 ArenaBlock
* pCurBlock
= m_pCurBlock
;
80 m_offset
= AlignUp(m_offset
, align
);
82 if ((m_offset
+ size
) <= pCurBlock
->blockSize
)
84 void* pMem
= PtrAdd(pCurBlock
->pMem
, m_offset
);
90 // Not enough memory in this block, fall through to allocate
94 static const size_t ArenaBlockSize
= 1024 * 1024;
95 size_t blockSize
= std::max
<size_t>(m_size
+ ArenaBlockSize
, std::max(size
, ArenaBlockSize
));
97 // Add in one BLOCK_ALIGN unit to store ArenaBlock in.
98 blockSize
= AlignUp(blockSize
+ ARENA_BLOCK_ALIGN
, ARENA_BLOCK_ALIGN
);
100 void *pMem
= m_allocator
.AllocateAligned(blockSize
, ARENA_BLOCK_ALIGN
); // Arena blocks are always simd byte aligned.
101 SWR_ASSERT(pMem
!= nullptr);
103 ArenaBlock
* pNewBlock
= new (pMem
) ArenaBlock();
105 if (pNewBlock
!= nullptr)
108 pNewBlock
->pNext
= m_pCurBlock
;
110 m_pCurBlock
= pNewBlock
;
111 m_pCurBlock
->pMem
= PtrAdd(pMem
, ARENA_BLOCK_ALIGN
);
112 m_pCurBlock
->blockSize
= blockSize
- ARENA_BLOCK_ALIGN
;
115 return AllocAligned(size
, align
);
118 void* Alloc(size_t size
)
120 return AllocAligned(size
, 1);
123 void* AllocAlignedSync(size_t size
, size_t align
)
125 void* pAlloc
= nullptr;
128 pAlloc
= AllocAligned(size
, align
);
134 void* AllocSync(size_t size
)
136 void* pAlloc
= nullptr;
139 pAlloc
= Alloc(size
);
145 void Reset(bool removeAll
= false)
151 ArenaBlock
*pUsedBlocks
= m_pCurBlock
->pNext
;
152 m_pCurBlock
->pNext
= nullptr;
155 ArenaBlock
* pBlock
= pUsedBlocks
;
156 pUsedBlocks
= pBlock
->pNext
;
158 m_allocator
.Free(pBlock
);
163 m_allocator
.Free(m_pCurBlock
);
164 m_pCurBlock
= nullptr;
171 size_t Size() const { return m_size
; }
175 ArenaBlock
* m_pCurBlock
= nullptr;
179 /// @note Mutex is only used by sync allocation functions.
182 DefaultAllocator m_defAllocator
;
187 using Arena
= TArena
<std::mutex
, T
>;
188 using StdArena
= Arena
<DefaultAllocator
>;
196 // Ref counted Arena for ArenaAllocator
198 struct RefArena
: TArena
<NullMutex
>
200 uint32_t AddRef() { return ++m_refCount
; }
201 uint32_t Release() { if (--m_refCount
) { return m_refCount
; } delete this; return 0; }
203 void* allocate(std::size_t n
)
209 void deallocate(void* p
) { --m_numAllocations
; }
210 void clear() { SWR_ASSERT(0 == m_numAllocations
); Reset(); }
213 uint32_t m_refCount
= 0;
214 uint32_t m_numAllocations
= 0;
217 #if 0 // THIS DOESN'T WORK!!!
218 // Arena based replacement for std::allocator
219 template <typename T
>
220 struct ArenaAllocator
222 typedef T value_type
;
225 m_pArena
= new RefArena();
230 m_pArena
->Release(); m_pArena
= nullptr;
232 ArenaAllocator(const ArenaAllocator
& copy
)
234 m_pArena
= const_cast<RefArena
*>(copy
.m_pArena
); m_pArena
->AddRef();
238 template <class U
> ArenaAllocator(const ArenaAllocator
<U
>& copy
)
240 m_pArena
= const_cast<RefArena
*>(copy
.m_pArena
); m_pArena
->AddRef();
242 T
* allocate(std::size_t n
)
246 sprintf_s(buf
, "Alloc: %lld\n", n
);
247 OutputDebugStringA(buf
);
249 void* p
= m_pArena
->allocate(n
* sizeof(T
));
250 return static_cast<T
*>(p
);
252 void deallocate(T
* p
, std::size_t n
)
256 sprintf_s(buf
, "Dealloc: %lld\n", n
);
257 OutputDebugStringA(buf
);
259 m_pArena
->deallocate(p
);
261 void clear() { m_pArena
->clear(); }
263 RefArena
* m_pArena
= nullptr;
266 template <class T
, class U
>
267 bool operator== (const ArenaAllocator
<T
>&, const ArenaAllocator
<U
>&)
272 template <class T
, class U
>
273 bool operator!= (const ArenaAllocator
<T
>&, const ArenaAllocator
<U
>&)