forked from sakjain92/Fractional-GPUs
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathallocator.cpp
More file actions
353 lines (269 loc) · 9.07 KB
/
allocator.cpp
File metadata and controls
353 lines (269 loc) · 9.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
* This is a custom allocator to manage memory of a given buffer
* without read/writing on the buffer.
* Currently it is a naive implementation using 'next-fit' logic (single threaded)
*/
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <map>
#include <fgpu_internal_allocator.hpp>
/* Represents a chunk of memory */
typedef struct node {
Q_NEW_LINK(node) all_link;
Q_NEW_LINK(node) free_link;
void *address;
size_t size;
bool is_free;
} node_t;
/* Head for the queue of nodes */
Q_NEW_HEAD(node_list, node);
/* Represents the context saved for an allocator */
typedef struct allocator {
struct node_list free_list; /* List of free nodes */
struct node_list all_list; /* List of all nodes */
void *start_address;
size_t size;
size_t alignment; /* Alignment requirement for all addresses */
std::map<void *, node_t *> nodes_map; /* Nodes are both mainted in map and list */
} allocator_t;
/* Does sanity check on a node */
static void do_node_sanity_check(allocator_t *ctx, node_t *node)
{
assert((uintptr_t)node->address % ctx->alignment == 0);
assert(node->size % ctx->alignment == 0);
}
/* Creates a new free node, starting at 'address' and of size 'size' */
static node_t *new_free_node(allocator_t *ctx, void *address, size_t size)
{
node_t *node;
node = new node_t;
Q_INIT_ELEM(node, free_link);
Q_INIT_ELEM(node, all_link);
node->address = address;
node->size = size;
node->is_free = false;
do_node_sanity_check(ctx, node);
return node;
}
/* Mark a node as allocated and remove from free list */
static void mark_node_allocated(allocator_t *ctx, node_t *node)
{
do_node_sanity_check(ctx, node);
assert(node->is_free);
Q_REMOVE(&ctx->free_list, node, free_link);
node->is_free = false;
}
/* Merges free nodes */
static void merge_nodes(allocator_t *ctx, node_t *node, node_t *prev, node_t *next)
{
assert(node);
assert(node->is_free);
do_node_sanity_check(ctx, node);
if (prev) {
assert(prev->is_free);
assert((uintptr_t)prev->address + prev->size == (uintptr_t)node->address);
do_node_sanity_check(ctx, prev);
}
if (next) {
assert(next->is_free);
assert((uintptr_t)node->address + node->size == (uintptr_t)next->address);
do_node_sanity_check(ctx, next);
}
if (prev && next) {
/* Consolidate all into prev node */
prev->size += node->size + next->size;
Q_REMOVE(&ctx->all_list, node, all_link);
Q_REMOVE(&ctx->all_list, next, all_link);
Q_REMOVE(&ctx->free_list, node, free_link);
Q_REMOVE(&ctx->free_list, next, free_link);
ctx->nodes_map.erase(node->address);
ctx->nodes_map.erase(next->address);
delete node;
delete next;
} else if (prev) {
/* Consolidate all into prev node */
prev->size += node->size;
Q_REMOVE(&ctx->all_list, node, all_link);
Q_REMOVE(&ctx->free_list, node, free_link);
ctx->nodes_map.erase(node->address);
delete node;
} else if (next) {
/* Consolidate all into current node */
node->size += next->size;
Q_REMOVE(&ctx->all_list, next, all_link);
Q_REMOVE(&ctx->free_list, next, free_link);
ctx->nodes_map.erase(next->address);
delete next;
}
}
/*
* Mark already existing node as free node
* This function takes care of merging nodes
*/
static void mark_node_free(allocator_t *ctx, node_t *node)
{
node_t *prev, *next;
do_node_sanity_check(ctx, node);
assert(!node->is_free);
Q_CHECK_REMOVED(node, free_link);
node->is_free = true;
prev = Q_GET_PREV(node, all_link);
next = Q_GET_NEXT(node, all_link);
/* Try to merge */
if (prev && next && (prev->is_free || next->is_free)) {
if (prev->is_free && next->is_free) {
Q_INSERT_AFTER(&ctx->free_list, prev, node, free_link);
merge_nodes(ctx, node, prev, next);
} else if (prev->is_free) {
Q_INSERT_AFTER(&ctx->free_list, prev, node, free_link);
merge_nodes(ctx, node, prev, NULL);
} else {
assert(next->is_free);
Q_INSERT_BEFORE(&ctx->free_list, next, node, free_link);
merge_nodes(ctx, node, NULL, next);
}
} else if (prev && prev->is_free) {
Q_INSERT_AFTER(&ctx->free_list, prev, node, free_link);
merge_nodes(ctx, node, prev, NULL);
} else if (next && next->is_free) {
Q_INSERT_BEFORE(&ctx->free_list, next, node, free_link);
merge_nodes(ctx, node, NULL, next);
} else if (!prev && !next) {
/* Node is the first node */
Q_INSERT_FRONT(&ctx->all_list, node, all_link);
Q_INSERT_FRONT(&ctx->free_list, node, free_link);
ctx->nodes_map[node->address] = node;
} else {
/*
* Insert node in appropriate place in the free list.
* Both prev and next are not free so don't know the appropriate place
* in queue.
* TODO: This is O(N). Try to reduce this.
*/
node_t *prev_free_node = NULL;
Q_FOREACH_PREV_FROM(prev_free_node, node, &ctx->all_list, all_link) {
if (prev_free_node->is_free)
break;
}
if (prev_free_node == NULL) {
Q_INSERT_FRONT(&ctx->free_list, node, free_link);
} else {
Q_INSERT_AFTER(&ctx->free_list, prev_free_node, node, free_link);
}
}
}
/*
* Splits the node into two node such that node's size is 'size'
*/
static void split_node(allocator_t *ctx, node_t *node, size_t size)
{
node_t *next;
uintptr_t next_address;
size_t next_size;
do_node_sanity_check(ctx, node);
assert(node->size > size);
assert(node->is_free);
next_size = node->size - size;
node->size = size;
next_address = (uintptr_t)node->address + node->size;
next = new_free_node(ctx, (void *)next_address, next_size);
next->is_free = true;
Q_INSERT_AFTER(&ctx->all_list, node, next, all_link);
Q_INSERT_AFTER(&ctx->free_list, node, next, free_link);
ctx->nodes_map[(void *)next_address] = next;
do_node_sanity_check(ctx, node);
do_node_sanity_check(ctx, next);
}
/*
* Finds a node of atleast size 'size'.
* Splits nodes if needed.
* Currently naive implementation searches over all the free nodes till first
* sufficiently large chunk is found.
* Returns NULL if no free node of appropriate size is found.
*/
static node_t *get_free_node(allocator_t *ctx, size_t size)
{
node_t *node = NULL;
size_t allocation_size;
/* Round up allocation size */
allocation_size = (size + ctx->alignment - 1) & ~(ctx->alignment - 1);
Q_FOREACH(node, &ctx->free_list, free_link) {
if (node->size >= allocation_size) {
break;
}
}
if (!node)
return NULL;
assert(node->is_free);
if (node->size > allocation_size) {
split_node(ctx, node, allocation_size);
}
mark_node_allocated(ctx, node);
return node;
}
/*
* Initializes an allocator context.
* Buf points to the start address.
* Size denotes total size of buffer.
* Alignment denotes the alignment of all subsequent allocations.
* Returns 0 on success, otherwise failure
*/
allocator_t *allocator_init(void *buf, size_t size, size_t alignment)
{
uintptr_t start_address, round_address;
size_t mask = alignment - 1;
allocator_t *ctx;
size_t node_size;
node_t *node;
/* Alignment should be power of 2 */
if (alignment & mask) {
return NULL;
}
start_address = (uintptr_t)buf;
round_address = (start_address + alignment - 1) & ~mask;
if (size < round_address - start_address) {
return NULL;
}
/* Round down size and also take into account the rouding up effect above */
node_size = size - (round_address - start_address);
node_size = node_size & ~mask;
ctx = new allocator_t;
Q_INIT_HEAD(&ctx->free_list);
Q_INIT_HEAD(&ctx->all_list);
ctx->size = size;
ctx->start_address = buf;
ctx->alignment = alignment;
/* Insert the whole buffer as a free node */
node = new_free_node(ctx, (void *)round_address, node_size);
mark_node_free(ctx, node);
return ctx;
}
/* Allocated a buffer of atleast size 'size' and returns it */
void *allocator_alloc(allocator_t *ctx, size_t size)
{
node_t *node;
node = get_free_node(ctx, size);
if (!node)
return NULL;
return node->address;
}
/* Frees up a buffer */
void allocator_free(allocator_t *ctx, void *address)
{
std::map<void *, node_t *>::iterator it;
node_t *node;
it = ctx->nodes_map.find(address);
assert(it != ctx->nodes_map.end());
node = it->second;
mark_node_free(ctx, node);
}
/* Frees up the allocator */
void allocator_deinit(allocator_t *ctx)
{
std::map<void *, node_t *>::iterator it;
for (it = ctx->nodes_map.begin(); it != ctx->nodes_map.end(); ++it)
delete it->second;
ctx->nodes_map.clear();
delete ctx;
}