vsg 1.1.10
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 <vector>
19
20namespace vsg
21{
23 //
24 // InstrusiveAllocator is the default Allocator implenentation
25 //
26 // Memory is allocated for fixed sized blocks, with indexing of allocated and available slots of memory
27 // are stored within the same memory block that user memory allocation are made from. The memory block
28 // is created a contiguous block of 4 bytes Elements, where the Element is a union of bitfield linked list
29 // market the beginning of the previous slot or the begging of the next, the status of whether the slot is
30 // allocated or available, or an index when used as part of doubling linked list of free slots.
31 //
32 // The block allocation is done based on the type of object so all nodes, data or general objects are
33 // allocated within the blocks containing objects of similar type. This form of block allocation helps
34 // scene graph traversal speeds by improving cache coherency/reducing cache missing as it ensures that
35 // nodes etc. are packed in adjacent memory.
36 //
37 // The instrusive indexing means there is only a 4 byte panalty for each memory allocation, and a minimum
38 // memory use per allocation of 12 bytes (3 Elements - 1 for the slot{previous, next, status} and 2 for the
39 // previous and next free list indices.
40 //
41 // The maximum size of allocations within the block allocation is (2^15-2) * 4, allocations larger than this
42 // are allocated using aligned versions of std::new and std::delete.
43 //
44 class VSG_DECLSPEC IntrusiveAllocator : public Allocator
45 {
46 public:
47 explicit IntrusiveAllocator(size_t in_defaultAlignment = 8);
48 explicit IntrusiveAllocator(std::unique_ptr<Allocator> in_nestedAllocator, size_t in_defaultAlignment = 8);
49
50 ~IntrusiveAllocator();
51
52 void report(std::ostream& out) const override;
53
54 void* allocate(std::size_t size, AllocatorAffinity allocatorAffinity = ALLOCATOR_AFFINITY_OBJECTS) override;
55
56 bool deallocate(void* ptr, std::size_t size) override;
57
58 bool validate() const;
59
60 size_t deleteEmptyMemoryBlocks() override;
61 size_t totalAvailableSize() const override;
62 size_t totalReservedSize() const override;
63 size_t totalMemorySize() const override;
64 void setBlockSize(AllocatorAffinity allocatorAffinity, size_t blockSize) override;
65
66 protected:
67 struct VSG_DECLSPEC MemoryBlock
68 {
69 MemoryBlock(const std::string& in_name, size_t in_blockSize, size_t in_alignment);
70 virtual ~MemoryBlock();
71
72 std::string name;
73
74 void* allocate(std::size_t size);
75 bool deallocate(void* ptr, std::size_t size);
76
77 void report(std::ostream& out) const;
78
79 // bitfield packing of doubly-linked with status field into a 4 byte word
80 struct Element
81 {
82 union
83 {
84 uint32_t index;
85
86 struct
87 {
88 unsigned int previous : 15;
89 unsigned int next : 15;
90 unsigned int status : 2;
91 };
92 };
93
94 using Offset = decltype(previous);
95 using Status = decltype(status);
96 using Index = decltype(index);
97
98 explicit Element(Index in_index) :
99 index(static_cast<Offset>(in_index)) {}
100
101 Element(Offset in_previous, Offset in_next, Status in_status) :
102 previous(static_cast<Offset>(in_previous)),
103 next(static_cast<Offset>(in_next)),
104 status(in_status) {}
105
106 Element() = default;
107 Element(const Element&) = default;
108 };
109
110 struct FreeList
111 {
112 Element::Index count = 0;
113 Element::Index head = 0;
114 };
115
116 Element* memory = nullptr;
117 Element* memoryEnd = nullptr;
118
119 size_t alignment = 8; // min aligment is 4 { sizeof(Element) }
120 size_t blockAlignment = 16;
121 size_t blockSize = 0;
122 size_t maximumAllocationSize = 0;
123 Element::Index elementAlignment = 1;
124 Element::Index firstSlot = 1;
125 Element::Index capacity = 0;
126
127 std::vector<FreeList> freeLists;
128
129 bool validate() const;
130
131 bool freeSlotsAvaible(size_t size) const;
132
133 inline bool within(const void* ptr) const { return memory <= ptr && ptr < memoryEnd; }
134
135 size_t totalAvailableSize() const;
136 size_t totalReservedSize() const;
137 size_t totalMemorySize() const;
138
139 // used for debugging only.
140 struct VSG_DECLSPEC SlotTester
141 {
142 SlotTester(Element* in_mem, size_t in_head) :
143 mem(in_mem), head(in_head){};
144
145 const Element* mem = nullptr;
146 size_t head = 0;
147
148 struct Entry
149 {
150 std::string name;
151 size_t position;
152 Element slot;
153 size_t previousFree;
154 size_t nextFree;
155 };
156
157 std::list<Entry> elements;
158
159 void slot(size_t position, const std::string& name);
160
161 void report(std::ostream& out);
162 };
163
164 static inline size_t computeMaxiumAllocationSize(size_t blockSize, size_t alignment)
165 {
166 return std::min(blockSize - alignment, size_t((1 << 15) - 2) * sizeof(Element));
167 }
168 };
169
170 class VSG_DECLSPEC MemoryBlocks
171 {
172 public:
173 MemoryBlocks(IntrusiveAllocator* in_parent, const std::string& in_name, size_t in_blockSize, size_t in_alignment);
174 virtual ~MemoryBlocks();
175
176 IntrusiveAllocator* parent = nullptr;
177 std::string name;
178 size_t alignment = 8;
179 size_t blockSize = 0;
180 size_t maximumAllocationSize = 0;
181 std::vector<std::shared_ptr<MemoryBlock>> memoryBlocks;
182 std::shared_ptr<MemoryBlock> memoryBlockWithSpace;
183
184 void* allocate(std::size_t size);
185 void report(std::ostream& out) const;
186 bool validate() const;
187
188 size_t deleteEmptyMemoryBlocks();
189 size_t totalAvailableSize() const;
190 size_t totalReservedSize() const;
191 size_t totalMemorySize() const;
192 };
193
194 std::vector<std::unique_ptr<MemoryBlocks>> allocatorMemoryBlocks;
195 std::map<void*, std::shared_ptr<MemoryBlock>> memoryBlocks;
196 std::map<void*, std::pair<size_t, size_t>> largeAllocations;
197 };
198
199} // 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:81
Definition IntrusiveAllocator.h:111
Definition IntrusiveAllocator.h:149