vsg 1.1.13
VulkanSceneGraph library
Loading...
Searching...
No Matches
IntrusiveAllocator.h
1#pragma once
2
3/* <editor-fold desc="MIT License">
4
5Copyright(c) 2024 Robert Osfield
6
7Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
9The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10
11THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12
13</editor-fold> */
14
15#include <vsg/core/Allocator.h>
16
17#include <list>
18#include <string>
19#include <vector>
20
21namespace vsg
22{
24 //
25 // InstrusiveAllocator is the default Allocator implenentation
26 //
27 // Memory is allocated for fixed sized blocks, with indexing of allocated and available slots of memory
28 // are stored within the same memory block that user memory allocation are made from. The memory block
29 // is created a contiguous block of 4 bytes Elements, where the Element is a union of bitfield linked list
30 // market the beginning of the previous slot or the begging of the next, the status of whether the slot is
31 // allocated or available, or an index when used as part of doubling linked list of free slots.
32 //
33 // The block allocation is done based on the type of object so all nodes, data or general objects are
34 // allocated within the blocks containing objects of similar type. This form of block allocation helps
35 // scene graph traversal speeds by improving cache coherency/reducing cache missing as it ensures that
36 // nodes etc. are packed in adjacent memory.
37 //
38 // The instrusive indexing means there is only a 4 byte panalty for each memory allocation, and a minimum
39 // memory use per allocation of 12 bytes (3 Elements - 1 for the slot{previous, next, status} and 2 for the
40 // previous and next free list indices.
41 //
42 // The maximum size of allocations within the block allocation is (2^15-2) * 4, allocations larger than this
43 // are allocated using aligned versions of std::new and std::delete.
44 //
45 class VSG_DECLSPEC IntrusiveAllocator : public Allocator
46 {
47 public:
48 explicit IntrusiveAllocator(size_t in_defaultAlignment = 8);
49 explicit IntrusiveAllocator(std::unique_ptr<Allocator> in_nestedAllocator, size_t in_defaultAlignment = 8);
50
51 ~IntrusiveAllocator();
52
53 void report(std::ostream& out) const override;
54
55 void* allocate(std::size_t size, AllocatorAffinity allocatorAffinity = ALLOCATOR_AFFINITY_OBJECTS) override;
56
57 bool deallocate(void* ptr, std::size_t size) override;
58
59 bool validate() const;
60
61 size_t deleteEmptyMemoryBlocks() override;
62 size_t totalAvailableSize() const override;
63 size_t totalReservedSize() const override;
64 size_t totalMemorySize() const override;
65 void setBlockSize(AllocatorAffinity allocatorAffinity, size_t blockSize) override;
66
67 protected:
68 struct VSG_DECLSPEC MemoryBlock
69 {
70 MemoryBlock(const std::string& in_name, size_t in_blockSize, size_t in_alignment);
71 virtual ~MemoryBlock();
72
73 std::string name;
74
75 void* allocate(std::size_t size);
76 bool deallocate(void* ptr, std::size_t size);
77
78 void report(std::ostream& out) const;
79
80 // bitfield packing of doubly-linked with status field into a 4 byte word
81 struct Element
82 {
83 union
84 {
85 uint32_t index;
86
87 struct
88 {
89 unsigned int previous : 15;
90 unsigned int next : 15;
91 unsigned int status : 2;
92 };
93 };
94
95 using Offset = decltype(previous);
96 using Status = decltype(status);
97 using Index = decltype(index);
98
99 explicit Element(Index in_index) :
100 index(static_cast<Offset>(in_index)) {}
101
102 Element(Offset in_previous, Offset in_next, Status in_status) :
103 previous(static_cast<Offset>(in_previous)),
104 next(static_cast<Offset>(in_next)),
105 status(in_status) {}
106
107 Element() = default;
108 Element(const Element&) = default;
109 Element& operator=(const Element&) = default;
110 };
111
112 struct FreeList
113 {
114 Element::Index count = 0;
115 Element::Index head = 0;
116 };
117
118 Element* memory = nullptr;
119 Element* memoryEnd = nullptr;
120
121 size_t alignment = 8; // min alignment is 4 { sizeof(Element) }
122 size_t blockAlignment = 16;
123 size_t blockSize = 0;
124 size_t maximumAllocationSize = 0;
125 Element::Index elementAlignment = 1;
126 Element::Index firstSlot = 1;
127 Element::Index capacity = 0;
128
129 std::vector<FreeList> freeLists;
130
131 bool validate() const;
132
133 bool freeSlotsAvaible(size_t size) const;
134
135 inline bool within(const void* ptr) const { return memory <= ptr && ptr < memoryEnd; }
136
137 size_t totalAvailableSize() const;
138 size_t totalReservedSize() const;
139 size_t totalMemorySize() const;
140
141 // used for debugging only.
142 struct VSG_DECLSPEC SlotTester
143 {
144 SlotTester(Element* in_mem, size_t in_head) :
145 mem(in_mem), head(in_head){};
146
147 const Element* mem = nullptr;
148 size_t head = 0;
149
150 struct Entry
151 {
152 std::string name;
153 size_t position;
154 Element slot;
155 size_t previousFree;
156 size_t nextFree;
157 };
158
159 std::list<Entry> elements;
160
161 void slot(size_t position, const std::string& name);
162
163 void report(std::ostream& out);
164 };
165
166 static inline size_t computeMaxiumAllocationSize(size_t blockSize, size_t alignment)
167 {
168 return std::min(blockSize - alignment, size_t((1 << 15) - 2) * sizeof(Element));
169 }
170 };
171
172 class VSG_DECLSPEC MemoryBlocks
173 {
174 public:
175 MemoryBlocks(IntrusiveAllocator* in_parent, const std::string& in_name, size_t in_blockSize, size_t in_alignment);
176 virtual ~MemoryBlocks();
177
178 IntrusiveAllocator* parent = nullptr;
179 std::string name;
180 size_t alignment = 8;
181 size_t blockSize = 0;
182 size_t maximumAllocationSize = 0;
183 std::vector<std::shared_ptr<MemoryBlock>> memoryBlocks;
184 std::shared_ptr<MemoryBlock> memoryBlockWithSpace;
185
186 void* allocate(std::size_t size);
187 void report(std::ostream& out) const;
188 bool validate() const;
189
190 size_t deleteEmptyMemoryBlocks();
191 size_t totalAvailableSize() const;
192 size_t totalReservedSize() const;
193 size_t totalMemorySize() const;
194 };
195
196 std::vector<std::unique_ptr<MemoryBlocks>> allocatorMemoryBlocks;
197 std::map<void*, std::shared_ptr<MemoryBlock>> memoryBlocks;
198 std::map<void*, std::pair<size_t, size_t>> largeAllocations;
199 };
200
201} // namespace vsg
size_t totalMemorySize() const override
return the total memory size of allocated MemoryBlocks
size_t totalReservedSize() const override
return the total reserved size of allocated MemoryBlocks
void * allocate(std::size_t size, AllocatorAffinity allocatorAffinity=ALLOCATOR_AFFINITY_OBJECTS) override
allocate from the pool of memory blocks, or allocate from a new memory block
void report(std::ostream &out) const override
report stats about blocks of memory allocated.
bool deallocate(void *ptr, std::size_t size) override
deallocate, returning data to pool.
size_t totalAvailableSize() const override
return the total available size of allocated MemoryBlocks
size_t deleteEmptyMemoryBlocks() override
delete any MemoryBlock that are empty
Definition IntrusiveAllocator.h:82
Definition IntrusiveAllocator.h:113
Definition IntrusiveAllocator.h:151