Skip to content

Commit 1f42ebb

Browse files
full and complete draft of the new SimpleBlockBasedAllocator
1 parent 616c3e9 commit 1f42ebb

5 files changed

Lines changed: 156 additions & 127 deletions

File tree

include/nbl/asset/ICPUScene.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#define _NBL_ASSET_I_CPU_SCENE_H_INCLUDED_
66

77

8+
// TODO: not memory pool but ... object pool
89
#include "nbl/core/containers/CMemoryPool.h"
910

1011
#include "nbl/asset/IScene.h"

include/nbl/core/algorithm/utility.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct type_list_size<type_list<T...>> : std::integral_constant<size_t,sizeof...
1818
template<typename TypeList>
1919
inline constexpr size_t type_list_size_v = type_list_size<TypeList>::value;
2020

21+
//
2122
template<template<typename> class, typename TypeList>
2223
struct filter;
2324
template<template<typename> class Pred, typename... T>
@@ -42,6 +43,13 @@ struct filter<Pred,type_list<T,Ts...>>
4243
template<template<typename> class Pred, typename TypeList>
4344
using filter_t = filter<Pred,TypeList>::type;
4445

46+
// to work around `std::identity` not matching
47+
namespace impl
48+
{
49+
template<typename T>
50+
class identitiy {};
51+
}
52+
//
4553
template<template<class...> class ListLikeOutT, template<class> class X, typename ListLike>
4654
struct list_transform
4755
{
@@ -52,9 +60,20 @@ struct list_transform
5260
public:
5361
using type = decltype(_impl(std::declval<ListLike>()));
5462
};
63+
template<template<class...> class ListLikeOutT, typename ListLike>
64+
struct list_transform<ListLikeOutT,impl::identitiy,ListLike>
65+
{
66+
private:
67+
template<template<class...> class ListLikeInT, typename... T>
68+
static ListLikeOutT<T...> _impl(const ListLikeInT<T...>&);
69+
70+
public:
71+
using type = decltype(_impl(std::declval<ListLike>()));
72+
};
5573
template<template<class...> class ListLikeOutT, template<class> class X, typename ListLike>
5674
using list_transform_t = list_transform<ListLikeOutT,X,ListLike>::type;
5775

76+
//
5877
template<template<class> class X, typename ListLike>
5978
using tuple_transform_t = list_transform_t<std::tuple,X,ListLike>;
6079

include/nbl/core/alloc/SimpleBlockBasedAllocator.h

Lines changed: 123 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "nbl/core/alloc/address_allocator_traits.h"
1111
#include "nbl/core/alloc/AddressAllocatorConcurrencyAdaptors.h"
1212

13+
#include "gtl/btree.hpp"
14+
1315
#include <memory>
1416
#include <mutex>
1517

@@ -24,202 +26,201 @@ class SimpleBlockBasedAllocator final
2426
{
2527
public:
2628
using addr_alloc_traits = address_allocator_traits<AddressAllocator>;
29+
using extra_params_t = tuple_transform_t<impl::identitiy,addr_alloc_traits::extra_ctor_param_types>;
2730
using size_type = typename addr_alloc_traits::size_type;
2831
// The blocks will be allocated aligned to at least this value
2932
constexpr static inline size_type meta_alignment = 64u;
3033

3134
private:
32-
using extra_params_t = tuple_transform_t<std::type_identity,addr_alloc_traits::extra_ctor_param_types>;
35+
using this_t = SimpleBlockBasedAllocator<AddressAllocator>;
3336
// Blocks get allocated/deallocated on demand, inside there's a suballocator.
3437
// Blocks are always the same size, and obviously can't allocate anything larger than themselves.
35-
class Block
38+
class alignas(16) Block final
3639
{
40+
friend class this_t;
41+
3742
AddressAllocator addrAlloc;
3843

39-
public:
40-
template<typename... Args>
41-
inline Block(const size_type blockSize, const Args&... args) :
42-
addrAlloc(AddressAllocator(data()+blockSize,./*no address offset*/0u,/*no alignment offset needed*/0u,meta_alignment,blockSize,args...))
44+
/* if std::apply doesn't work vause of deduction kicking in https://stackoverflow.com/questions/79893672/using-stdapply-in-a-class-template
45+
*
46+
* use https://www.fluentcpp.com/2021/03/05/stdindex_sequence-and-its-improvement-in-c20/ instead
47+
template<int ...> struct seq {};
48+
template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { };
49+
template<int ...S> struct gens<0, S...> { typedef seq<S...> type; };
50+
51+
template<int ...S>
52+
void constructBlock(Block* mem,seq<S...>)
4353
{
44-
assert(addr_alloc_traits::get_align_offset(addrAlloc) == 0ul);
45-
assert(addr_alloc_traits::get_combined_offset(addrAlloc) == 0u);
54+
std::construct_at(mem,m_blockSize,std::get<S>(blockCreationArgs)...);
4655
}
56+
template<typename... Ts>
57+
static inline computeReservedSize(const core::tuple<Ts...>& _addrCreationArgs)
58+
{
59+
return addr_alloc_traits::reserved_size(meta_alignment,blockSize,std::get<>(_addrCreationArgs)...);
60+
}
61+
*/
4762

48-
template<typename... Args>
49-
static size_type size_of(size_type blockSize, const Args&... args)
63+
public:
64+
struct SCreationParams
5065
{
51-
return core::alignUp(sizeof(AddressAllocator),meta_alignment)+blockSize+addr_alloc_traits::reserved_size(meta_alignment,blockSize,args...);
66+
inline SCreationParams(const size_type _blockSize, const extra_params_t& extraArgs) : addrCreationArgs(extraArgs), blockSize(_blockSize),
67+
reservedSize(std::apply([blockSize]<typename... Args>(const Args&... args)->size_type
68+
{
69+
return addr_alloc_traits::reserved_size(meta_alignment,blockSize,args...);
70+
},addrCreationArgs)
71+
), totalSize(core::alignUp(sizeof(Block)+reservedSize,meta_alignment)+blockSize) {}
72+
73+
const extra_params_t addrCreationArgs;
74+
const size_type blockSize;
75+
const size_type reservedSize;
76+
const size_type totalSize;
77+
};
78+
//
79+
inline Block(const SCreationParams& params) : addrAlloc(AddressAllocator())
80+
{
81+
std::apply([&params]<typename... Args>(const Args&... args)->void
82+
{
83+
addrAlloc = AddressAllocator(this+1,./*no address offset*/0u,/*no alignment offset needed*/0u,meta_alignment,params.blockSize,args...);
84+
},params.addrCreationArgs
85+
);
86+
assert(addr_alloc_traits::get_align_offset(addrAlloc) == 0ul);
87+
assert(addr_alloc_traits::get_combined_offset(addrAlloc) == 0u);
5288
}
5389

54-
uint8_t* data() { return reinterpret_cast<uint8_t*>(this)+core::alignUp(sizeof(AddressAllocator),meta_alignment); }
55-
const uint8_t* data() const
90+
inline uint8_t* data(const SCreationParams& params) {return reinterpret_cast<uint8_t*>(this)+params.totalSize-params.blockSize;}
91+
inline const uint8_t* data() const
5692
{
5793
return const_cast<Block*>(this)->data();
5894
}
5995

60-
const AddressAllocator& getAllocator() const { return addrAlloc; }
96+
const AddressAllocator& getAllocator() const {return addrAlloc;}
6197

6298
size_type alloc(size_type bytes, size_type alignment)
6399
{
64100
size_type addr = AddressAllocator::invalid_address;
65-
addr_alloc_traits::multi_alloc_addr(addrAlloc, 1u, &addr, &bytes, &alignment);
101+
addr_alloc_traits::multi_alloc_addr(addrAlloc,1u,&addr,&bytes,&alignment);
66102
return addr;
67103
}
68104

69105
void free(size_type addr, size_type bytes)
70106
{
71-
addr_alloc_traits::multi_free_addr(addrAlloc, 1u, &addr, &bytes);
107+
addr_alloc_traits::multi_free_addr(addrAlloc,1u,&addr,&bytes);
72108
}
73109
};
74110

75111
public:
76112
virtual inline ~SimpleBlockBasedAllocator()
77113
{
78114
reset();
79-
metaAlloc.deallocate(blocks,maxBlockCount);
80115
}
81116

82-
template<typename... Args>
83-
inline SimpleBlockBasedAllocator(smart_refctd_ptr<refctd_memory_resource>&& _mem_resource, const size_type _blockSize, const size_type _initBlockCount, Args&&... args) :
84-
m_blockCreationArgs(std::forward<Args>(args)...), m_mem_resource(std::move(_mem_resource)), m_blockSize(_blockSize), m_effectiveBlockSize(Block::size_of(m_blockSize,m_blockCreationArgs))
117+
struct SCreationParams
85118
{
86-
// TODO: block init
87-
m_initBlockCount = std::max<size_type>(_initBlockCount,1));
88-
blocks = metaAlloc.allocate(maxBlockCount,meta_alignment))
89-
std::fill(blocks,blocks+maxBlockCount,nullptr);
90-
}
91-
92-
inline SimpleBlockBasedAllocator& operator=(SimpleBlockBasedAllocator&& other)
93-
{
94-
std::swap(m_blockCreationArgs,other.m_blockCreationArgs);
95-
std::swap(m_mem_resource,other.m_mem_resource);
96-
std::swap(m_blockSize,other.m_blockSize);
97-
std::swap(m_effectiveBlockSize,other.m_effectiveBlockSize);
98-
// TODO: block swap
99-
return *this;
100-
}
101-
inline SimpleBlockBasedAllocator(SimpleBlockBasedAllocator<AddressAllocator>&& other)
119+
smart_refctd_ptr<refctd_memory_resource> mem_resource;
120+
extra_params_t addrAllocCtorExtraParams;
121+
size_type blockSize = 128u<<10;
122+
size_type initBlockCount = 1;
123+
};
124+
inline SimpleBlockBasedAllocator(SCreationParams&& params) : m_blockCreationParams(params.blockSize,std::move(params.addrAllocCtorExtraParams)),
125+
m_initBlockCount(std::max<size_type>(_initBlockCount,1)), m_mem_resource(std::move(params.mem_resource))
102126
{
103-
operator=(std::move(other));
127+
m_blocks.reserve(m_initBlockCount);
128+
for (auto i=0u; i<m_initBlockCount; i++)
129+
m_blocks.insert(createBlock());
104130
}
105131

106-
inline void reset()
132+
// deallocates everything
133+
inline void reset()
107134
{
108-
// TODO: reset
109-
for (auto i=minBlockCount; i<maxBlockCount; i++)
110-
deleteBlock(i);
135+
assert(m_blocks.size()>=m_initBlockCount);
136+
auto it = m_blocks.begin();
137+
for (auto i=0u; i<m_initBlockCount; i++)
138+
{
139+
auto* block = *(it++);
140+
if (i<m_initBlockCount)
141+
block->getAllocator().reset();
142+
else
143+
deleteBlock(block);
144+
}
145+
m_blocks.erase(m_blocks.begin()+m_initBlockCount,m_blocks.end());
111146
}
112147

113-
114-
inline void* allocate(const size_type bytes, const size_type alignment) noexcept
148+
//
149+
inline void* allocate(const size_type bytes, const size_type alignment) noexcept
115150
{
116-
// TODO: rewrite
117151
constexpr auto invalid_address = AddressAllocator::invalid_address;
118-
for (size_type i=0u; i<maxBlockCount; i++)
152+
// TODO: better allocation strategies like tlsf
153+
for (auto& entry : m_blocks)
119154
{
120-
auto& block = blocks[i];
121-
122-
bool die = i==(maxBlockCount-1u);
123-
if (!block)
124-
{
125-
block = createBlock();
126-
die = true;
127-
}
128-
129-
size_type addr = block->alloc(bytes, alignment);
130-
if (addr == invalid_address)
131-
{
132-
if (die)
133-
break;
134-
else
135-
continue;
136-
}
137-
155+
auto* block = entry;
156+
if (const auto addr=block->alloc(bytes,alignment); addr!=invalid_address)
157+
return block->data()+addr;
158+
}
159+
auto* block = createBlock();
160+
if (const auto addr=block->alloc(bytes,alignment); addr!=invalid_address)
161+
{
162+
m_blocks.insert(block);
138163
return block->data()+addr;
139164
}
165+
else
166+
deleteBlock(block);
140167
return nullptr;
141168
}
142-
inline void deallocate(void* p, const size_type bytes) noexcept
169+
inline void deallocate(void* p, const size_type bytes) noexcept
143170
{
144-
// TODO: rewrite
145-
for (size_type i=0u; i<maxBlockCount; i++)
171+
assert(m_blocks.size()>=m_initBlockCount);
172+
auto found = m_blocks.lower_bound(reinterpret_cast<Block*>(p));
173+
assert(found!=m_blocks.end());
174+
auto* block = *found;
175+
assert(block->data()<=p && p<block->data()+m_blockCreationParams.blockSize);
176+
const size_type addr = reinterpret_cast<uint8_t*>(p)-block->data();
177+
block->free(addr,bytes);
178+
if (m_blocks.size()>m_initBlockCount && addr_alloc_traits::get_allocated_size(block->getAllocator())==size_type(0u))
146179
{
147-
auto& block = blocks[i];
148-
if (!block)
149-
continue;
150-
151-
size_type addr = reinterpret_cast<uint8_t*>(p)-block->data();
152-
if (addr<blockSize)
153-
{
154-
block->free(addr,bytes);
155-
if (i>=minBlockCount && addr_alloc_traits::get_allocated_size(block->getAllocator())==size_type(0u))
156-
deleteBlock(i);
157-
return;
158-
}
180+
deleteBlock(block);
181+
m_blocks.erase(found);
159182
}
160-
assert(false);
161183
}
162184

163-
inline bool operator!=(const SimpleBlockBasedAllocator<AddressAllocator,DataAllocator>& other) const noexcept
185+
inline bool operator!=(const this_t& other) const noexcept
164186
{
165-
// TODO: rewrite
166-
if (blockSize != other.blockSize)
167-
return true;
168-
if (effectiveBlockSize != other.effectiveBlockSize)
187+
if (m_blockCreationParams != other.m_blockCreationParams)
169188
return true;
170-
if (minBlockCount != other.minBlockCount)
189+
if (m_initBlockCount != other.m_initBlockCount)
171190
return true;
172-
if (maxBlockCount != other.maxBlockCount)
191+
if (m_mem_resource != other.m_mem_resource)
173192
return true;
174-
if (metaAlloc != other.metaAlloc)
175-
return true;
176-
if (blocks != other.blocks)
177-
return true;
178-
if (blockAlloc != other.blockAlloc)
193+
if (m_backBlock != other.m_backBlock)
179194
return true;
180195
return false;
181196
}
182-
inline bool operator==(const SimpleBlockBasedAllocator<AddressAllocator,DataAllocator>& other) const noexcept
197+
inline bool operator==(const this_t& other) const noexcept
183198
{
184199
return !operator!=(other);
185200
}
186201

187202
protected:
188-
extra_params_t m_blockCreationArgs;
189-
smart_refctd_ptr<refctd_memory_resource> m_mem_resource;
190-
size_type m_blockSize;
191-
size_type m_effectiveBlockSize;
192-
// TODO: rewrite this
193-
size_type m_initBlockCount;
194-
Block** blocks;
195-
196-
197-
template<int ...> struct seq {};
198-
template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { };
199-
template<int ...S> struct gens<0, S...> { typedef seq<S...> type; };
200-
201-
template<int ...S>
202-
void constructBlock(Block* mem,seq<S...>)
203+
using block_map_t = gtl::btree_set<Block*>;
204+
205+
inline Block* createBlock()
203206
{
204-
std::construct_at(mem,m_blockSize,std::get<S>(blockCreationArgs)...);
207+
auto* block = reinterpret_cast<Block*>(m_mem_resource->allocate(m_blockCreationParams.totalSize,meta_alignment));
208+
std::construct_at(block,m_blockCreationArgs);
209+
m_blocks.insert(block);
210+
return block;
205211
}
206-
Block* createBlock()
212+
inline void deleteBlock(Block* block)
207213
{
208-
auto retval = reinterpret_cast<Block*>(blockAlloc.allocate(effectiveBlockSize, meta_alignment));
209-
constructBlock(retval,typename gens<sizeof...(Args)>::type());
210-
return retval;
214+
assert(block);
215+
std::destroy_at(block);
216+
m_mem_resource->deallocate(block,m_blockCreationParams.totalSize,meta_alignment);
211217
}
212218

213-
214-
void deleteBlock(uint32_t index)
215-
{
216-
if (!blocks[index])
217-
return;
218-
219-
blocks[index]->~Block();
220-
blockAlloc.deallocate(reinterpret_cast<uint8_t*>(blocks[index]),effectiveBlockSize);
221-
blocks[index] = nullptr;
222-
}
219+
const Block::SCreationParams m_blockCreationParams;
220+
const uint32_t m_initBlockCount;
221+
smart_refctd_ptr<refctd_memory_resource> m_mem_resource;
222+
// TODO: better allocation strategies like tlsf
223+
block_map_t m_blocks;
223224
};
224225

225226
template<class AddressAllocator>

0 commit comments

Comments
 (0)