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 ([¶ms]<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
225226template <class AddressAllocator >
0 commit comments