Skip to content

Commit 08947d3

Browse files
reduzDarioSamo
authored andcommitted
Make RID_Owner lock-free for fetching.
This PR makes RID_Owner lock free for fetching values, this should give a very significant peformance boost where used. Some considerations: * A maximum number of elements to alocate must be given (by default 256k). * Access to the RID structure is still safe given they are independent from addition/removals. * RID access was never really thread-safe in the sense that the contents of the data are not protected anyway. Each server needs to implement locking as it sees fit.
1 parent 0a9d8f0 commit 08947d3

File tree

1 file changed

+68
-71
lines changed

1 file changed

+68
-71
lines changed

core/templates/rid_owner.h

+68-71
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
#define RID_OWNER_H
3333

3434
#include "core/os/memory.h"
35-
#include "core/os/spin_lock.h"
35+
#include "core/os/mutex.h"
3636
#include "core/string/print_string.h"
3737
#include "core/templates/hash_set.h"
3838
#include "core/templates/list.h"
@@ -69,42 +69,54 @@ class RID_AllocBase {
6969

7070
template <typename T, bool THREAD_SAFE = false>
7171
class RID_Alloc : public RID_AllocBase {
72-
T **chunks = nullptr;
72+
struct Chunk {
73+
T data;
74+
uint32_t validator;
75+
};
76+
Chunk **chunks = nullptr;
7377
uint32_t **free_list_chunks = nullptr;
74-
uint32_t **validator_chunks = nullptr;
7578

7679
uint32_t elements_in_chunk;
7780
uint32_t max_alloc = 0;
7881
uint32_t alloc_count = 0;
82+
uint32_t chunk_limit = 0;
7983

8084
const char *description = nullptr;
8185

82-
mutable SpinLock spin_lock;
86+
mutable Mutex mutex;
8387

8488
_FORCE_INLINE_ RID _allocate_rid() {
8589
if constexpr (THREAD_SAFE) {
86-
spin_lock.lock();
90+
mutex.lock();
8791
}
8892

8993
if (alloc_count == max_alloc) {
9094
//allocate a new chunk
9195
uint32_t chunk_count = alloc_count == 0 ? 0 : (max_alloc / elements_in_chunk);
96+
if (THREAD_SAFE && chunk_count == chunk_limit) {
97+
mutex.unlock();
98+
if (description != nullptr) {
99+
ERR_FAIL_V_MSG(RID(), vformat("Element limit for RID of type '%s' reached.", String(description)));
100+
} else {
101+
ERR_FAIL_V_MSG(RID(), "Element limit reached.");
102+
}
103+
}
92104

93105
//grow chunks
94-
chunks = (T **)memrealloc(chunks, sizeof(T *) * (chunk_count + 1));
95-
chunks[chunk_count] = (T *)memalloc(sizeof(T) * elements_in_chunk); //but don't initialize
96-
97-
//grow validators
98-
validator_chunks = (uint32_t **)memrealloc(validator_chunks, sizeof(uint32_t *) * (chunk_count + 1));
99-
validator_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
106+
if constexpr (!THREAD_SAFE) {
107+
chunks = (Chunk **)memrealloc(chunks, sizeof(Chunk *) * (chunk_count + 1));
108+
}
109+
chunks[chunk_count] = (Chunk *)memalloc(sizeof(Chunk) * elements_in_chunk); //but don't initialize
100110
//grow free lists
101-
free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1));
111+
if constexpr (!THREAD_SAFE) {
112+
free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1));
113+
}
102114
free_list_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
103115

104116
//initialize
105117
for (uint32_t i = 0; i < elements_in_chunk; i++) {
106118
// Don't initialize chunk.
107-
validator_chunks[chunk_count][i] = 0xFFFFFFFF;
119+
chunks[chunk_count][i].validator = 0xFFFFFFFF;
108120
free_list_chunks[chunk_count][i] = alloc_count + i;
109121
}
110122

@@ -122,14 +134,13 @@ class RID_Alloc : public RID_AllocBase {
122134
id <<= 32;
123135
id |= free_index;
124136

125-
validator_chunks[free_chunk][free_element] = validator;
126-
127-
validator_chunks[free_chunk][free_element] |= 0x80000000; //mark uninitialized bit
137+
chunks[free_chunk][free_element].validator = validator;
138+
chunks[free_chunk][free_element].validator |= 0x80000000; //mark uninitialized bit
128139

129140
alloc_count++;
130141

131142
if constexpr (THREAD_SAFE) {
132-
spin_lock.unlock();
143+
mutex.unlock();
133144
}
134145

135146
return _make_from_id(id);
@@ -156,16 +167,10 @@ class RID_Alloc : public RID_AllocBase {
156167
if (p_rid == RID()) {
157168
return nullptr;
158169
}
159-
if constexpr (THREAD_SAFE) {
160-
spin_lock.lock();
161-
}
162170

163171
uint64_t id = p_rid.get_id();
164172
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
165173
if (unlikely(idx >= max_alloc)) {
166-
if constexpr (THREAD_SAFE) {
167-
spin_lock.unlock();
168-
}
169174
return nullptr;
170175
}
171176

@@ -174,38 +179,26 @@ class RID_Alloc : public RID_AllocBase {
174179

175180
uint32_t validator = uint32_t(id >> 32);
176181

182+
Chunk &c = chunks[idx_chunk][idx_element];
177183
if (unlikely(p_initialize)) {
178-
if (unlikely(!(validator_chunks[idx_chunk][idx_element] & 0x80000000))) {
179-
if constexpr (THREAD_SAFE) {
180-
spin_lock.unlock();
181-
}
184+
if (unlikely(!(c.validator & 0x80000000))) {
182185
ERR_FAIL_V_MSG(nullptr, "Initializing already initialized RID");
183186
}
184187

185-
if (unlikely((validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) != validator)) {
186-
if constexpr (THREAD_SAFE) {
187-
spin_lock.unlock();
188-
}
188+
if (unlikely((c.validator & 0x7FFFFFFF) != validator)) {
189189
ERR_FAIL_V_MSG(nullptr, "Attempting to initialize the wrong RID");
190190
}
191191

192-
validator_chunks[idx_chunk][idx_element] &= 0x7FFFFFFF; //initialized
192+
c.validator &= 0x7FFFFFFF; //initialized
193193

194-
} else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
195-
if constexpr (THREAD_SAFE) {
196-
spin_lock.unlock();
197-
}
198-
if ((validator_chunks[idx_chunk][idx_element] & 0x80000000) && validator_chunks[idx_chunk][idx_element] != 0xFFFFFFFF) {
194+
} else if (unlikely(c.validator != validator)) {
195+
if ((c.validator & 0x80000000) && c.validator != 0xFFFFFFFF) {
199196
ERR_FAIL_V_MSG(nullptr, "Attempting to use an uninitialized RID");
200197
}
201198
return nullptr;
202199
}
203200

204-
T *ptr = &chunks[idx_chunk][idx_element];
205-
206-
if constexpr (THREAD_SAFE) {
207-
spin_lock.unlock();
208-
}
201+
T *ptr = &c.data;
209202

210203
return ptr;
211204
}
@@ -222,14 +215,14 @@ class RID_Alloc : public RID_AllocBase {
222215

223216
_FORCE_INLINE_ bool owns(const RID &p_rid) const {
224217
if constexpr (THREAD_SAFE) {
225-
spin_lock.lock();
218+
mutex.lock();
226219
}
227220

228221
uint64_t id = p_rid.get_id();
229222
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
230223
if (unlikely(idx >= max_alloc)) {
231224
if constexpr (THREAD_SAFE) {
232-
spin_lock.unlock();
225+
mutex.unlock();
233226
}
234227
return false;
235228
}
@@ -239,25 +232,25 @@ class RID_Alloc : public RID_AllocBase {
239232

240233
uint32_t validator = uint32_t(id >> 32);
241234

242-
bool owned = (validator != 0x7FFFFFFF) && (validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) == validator;
235+
bool owned = (validator != 0x7FFFFFFF) && (chunks[idx_chunk][idx_element].validator & 0x7FFFFFFF) == validator;
243236

244237
if constexpr (THREAD_SAFE) {
245-
spin_lock.unlock();
238+
mutex.unlock();
246239
}
247240

248241
return owned;
249242
}
250243

251244
_FORCE_INLINE_ void free(const RID &p_rid) {
252245
if constexpr (THREAD_SAFE) {
253-
spin_lock.lock();
246+
mutex.lock();
254247
}
255248

256249
uint64_t id = p_rid.get_id();
257250
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
258251
if (unlikely(idx >= max_alloc)) {
259252
if constexpr (THREAD_SAFE) {
260-
spin_lock.unlock();
253+
mutex.unlock();
261254
}
262255
ERR_FAIL();
263256
}
@@ -266,26 +259,26 @@ class RID_Alloc : public RID_AllocBase {
266259
uint32_t idx_element = idx % elements_in_chunk;
267260

268261
uint32_t validator = uint32_t(id >> 32);
269-
if (unlikely(validator_chunks[idx_chunk][idx_element] & 0x80000000)) {
262+
if (unlikely(chunks[idx_chunk][idx_element].validator & 0x80000000)) {
270263
if constexpr (THREAD_SAFE) {
271-
spin_lock.unlock();
264+
mutex.unlock();
272265
}
273-
ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID.");
274-
} else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
266+
ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID");
267+
} else if (unlikely(chunks[idx_chunk][idx_element].validator != validator)) {
275268
if constexpr (THREAD_SAFE) {
276-
spin_lock.unlock();
269+
mutex.unlock();
277270
}
278271
ERR_FAIL();
279272
}
280273

281-
chunks[idx_chunk][idx_element].~T();
282-
validator_chunks[idx_chunk][idx_element] = 0xFFFFFFFF; // go invalid
274+
chunks[idx_chunk][idx_element].data.~T();
275+
chunks[idx_chunk][idx_element].validator = 0xFFFFFFFF; // go invalid
283276

284277
alloc_count--;
285278
free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx;
286279

287280
if constexpr (THREAD_SAFE) {
288-
spin_lock.unlock();
281+
mutex.unlock();
289282
}
290283
}
291284

@@ -294,43 +287,49 @@ class RID_Alloc : public RID_AllocBase {
294287
}
295288
void get_owned_list(List<RID> *p_owned) const {
296289
if constexpr (THREAD_SAFE) {
297-
spin_lock.lock();
290+
mutex.lock();
298291
}
299292
for (size_t i = 0; i < max_alloc; i++) {
300-
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
293+
uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator;
301294
if (validator != 0xFFFFFFFF) {
302295
p_owned->push_back(_make_from_id((validator << 32) | i));
303296
}
304297
}
305298
if constexpr (THREAD_SAFE) {
306-
spin_lock.unlock();
299+
mutex.unlock();
307300
}
308301
}
309302

310303
//used for fast iteration in the elements or RIDs
311304
void fill_owned_buffer(RID *p_rid_buffer) const {
312305
if constexpr (THREAD_SAFE) {
313-
spin_lock.lock();
306+
mutex.lock();
314307
}
315308
uint32_t idx = 0;
316309
for (size_t i = 0; i < max_alloc; i++) {
317-
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
310+
uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator;
318311
if (validator != 0xFFFFFFFF) {
319312
p_rid_buffer[idx] = _make_from_id((validator << 32) | i);
320313
idx++;
321314
}
322315
}
316+
323317
if constexpr (THREAD_SAFE) {
324-
spin_lock.unlock();
318+
mutex.unlock();
325319
}
326320
}
327321

328322
void set_description(const char *p_descrption) {
329323
description = p_descrption;
330324
}
331325

332-
RID_Alloc(uint32_t p_target_chunk_byte_size = 65536) {
326+
RID_Alloc(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) {
333327
elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T));
328+
if constexpr (THREAD_SAFE) {
329+
chunk_limit = (p_maximum_number_of_elements / elements_in_chunk) + 1;
330+
chunks = (Chunk **)memalloc(sizeof(Chunk *) * chunk_limit);
331+
free_list_chunks = (uint32_t **)memalloc(sizeof(uint32_t *) * chunk_limit);
332+
}
334333
}
335334

336335
~RID_Alloc() {
@@ -339,27 +338,25 @@ class RID_Alloc : public RID_AllocBase {
339338
alloc_count, description ? description : typeid(T).name()));
340339

341340
for (size_t i = 0; i < max_alloc; i++) {
342-
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
341+
uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator;
343342
if (validator & 0x80000000) {
344343
continue; //uninitialized
345344
}
346345
if (validator != 0xFFFFFFFF) {
347-
chunks[i / elements_in_chunk][i % elements_in_chunk].~T();
346+
chunks[i / elements_in_chunk][i % elements_in_chunk].data.~T();
348347
}
349348
}
350349
}
351350

352351
uint32_t chunk_count = max_alloc / elements_in_chunk;
353352
for (uint32_t i = 0; i < chunk_count; i++) {
354353
memfree(chunks[i]);
355-
memfree(validator_chunks[i]);
356354
memfree(free_list_chunks[i]);
357355
}
358356

359357
if (chunks) {
360358
memfree(chunks);
361359
memfree(free_list_chunks);
362-
memfree(validator_chunks);
363360
}
364361
}
365362
};
@@ -419,8 +416,8 @@ class RID_PtrOwner {
419416
alloc.set_description(p_descrption);
420417
}
421418

422-
RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536) :
423-
alloc(p_target_chunk_byte_size) {}
419+
RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) :
420+
alloc(p_target_chunk_byte_size, p_maximum_number_of_elements) {}
424421
};
425422

426423
template <typename T, bool THREAD_SAFE = false>
@@ -473,8 +470,8 @@ class RID_Owner {
473470
void set_description(const char *p_descrption) {
474471
alloc.set_description(p_descrption);
475472
}
476-
RID_Owner(uint32_t p_target_chunk_byte_size = 65536) :
477-
alloc(p_target_chunk_byte_size) {}
473+
RID_Owner(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) :
474+
alloc(p_target_chunk_byte_size, p_maximum_number_of_elements) {}
478475
};
479476

480477
#endif // RID_OWNER_H

0 commit comments

Comments
 (0)