Skip to content

Commit d693070

Browse files
committed
replace chunks bool array with bit fields
1 parent f9719a6 commit d693070

File tree

3 files changed

+61
-50
lines changed

3 files changed

+61
-50
lines changed

src/pool/pool_disjoint.c

Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -75,28 +75,36 @@ static slab_t *create_slab(bucket_t *bucket) {
7575
umf_result_t res = UMF_RESULT_SUCCESS;
7676
umf_memory_provider_handle_t provider = bucket->pool->provider;
7777

78-
slab_t *slab = umf_ba_global_alloc(sizeof(*slab));
78+
size_t num_chunks_total =
79+
utils_max(bucket_slab_min_size(bucket) / bucket->size, 1);
80+
81+
// Calculate the number of 64-bit words needed.
82+
size_t num_words =
83+
(num_chunks_total + CHUNK_BITMAP_SIZE - 1) / CHUNK_BITMAP_SIZE;
84+
85+
slab_t *slab = umf_ba_global_alloc(sizeof(*slab) +
86+
num_words * sizeof(slab->chunks[0]));
7987
if (slab == NULL) {
8088
LOG_ERR("allocation of new slab failed!");
8189
return NULL;
8290
}
8391

8492
slab->num_chunks_allocated = 0;
85-
slab->first_free_chunk_idx = 0;
8693
slab->bucket = bucket;
8794

8895
slab->iter.val = slab;
8996
slab->iter.prev = slab->iter.next = NULL;
9097

91-
slab->num_chunks_total =
92-
utils_max(bucket_slab_min_size(bucket) / bucket->size, 1);
93-
slab->chunks =
94-
umf_ba_global_alloc(sizeof(*slab->chunks) * slab->num_chunks_total);
95-
if (slab->chunks == NULL) {
96-
LOG_ERR("allocation of slab chunks failed!");
97-
goto free_slab;
98+
slab->num_chunks_total = num_chunks_total;
99+
slab->num_words = num_words;
100+
101+
// set all chunks as free
102+
memset(slab->chunks, ~0, num_words * sizeof(slab->chunks[0]));
103+
if (num_chunks_total % CHUNK_BITMAP_SIZE) {
104+
// clear remaining bits
105+
slab->chunks[num_words - 1] =
106+
((1ULL << (num_chunks_total % CHUNK_BITMAP_SIZE)) - 1);
98107
}
99-
memset(slab->chunks, 0, sizeof(*slab->chunks) * slab->num_chunks_total);
100108

101109
// if slab_min_size is not a multiple of bucket size, we would have some
102110
// padding at the end of the slab
@@ -108,7 +116,7 @@ static slab_t *create_slab(bucket_t *bucket) {
108116
res = umfMemoryProviderAlloc(provider, slab->slab_size, 0, &slab->mem_ptr);
109117
if (res != UMF_RESULT_SUCCESS) {
110118
LOG_ERR("allocation of slab data failed!");
111-
goto free_slab_chunks;
119+
goto free_slab;
112120
}
113121

114122
// raw allocation is not available for user so mark it as inaccessible
@@ -117,9 +125,6 @@ static slab_t *create_slab(bucket_t *bucket) {
117125
LOG_DEBUG("bucket: %p, slab_size: %zu", (void *)bucket, slab->slab_size);
118126
return slab;
119127

120-
free_slab_chunks:
121-
umf_ba_global_free(slab->chunks);
122-
123128
free_slab:
124129
umf_ba_global_free(slab);
125130
return NULL;
@@ -136,25 +141,21 @@ static void destroy_slab(slab_t *slab) {
136141
LOG_ERR("deallocation of slab data failed!");
137142
}
138143

139-
umf_ba_global_free(slab->chunks);
140144
umf_ba_global_free(slab);
141145
}
142146

143-
// return the index of the first available chunk, SIZE_MAX otherwise
144147
static size_t slab_find_first_available_chunk_idx(const slab_t *slab) {
145-
// use the first free chunk index as a hint for the search
146-
for (bool *chunk = slab->chunks + slab->first_free_chunk_idx;
147-
chunk != slab->chunks + slab->num_chunks_total; chunk++) {
148-
149-
// false means not used
150-
if (*chunk == false) {
151-
size_t idx = chunk - slab->chunks;
152-
LOG_DEBUG("idx: %zu", idx);
153-
return idx;
148+
for (size_t i = 0; i < slab->num_words; i++) {
149+
// NOTE: free chunks are represented as set bits
150+
uint64_t word = slab->chunks[i];
151+
if (word != 0) {
152+
size_t bit_index = utils_lsb64(word);
153+
size_t free_chunk = i * CHUNK_BITMAP_SIZE + bit_index;
154+
return free_chunk;
154155
}
155156
}
156157

157-
LOG_DEBUG("idx: SIZE_MAX");
158+
// No free chunk was found.
158159
return SIZE_MAX;
159160
}
160161

@@ -167,12 +168,9 @@ static void *slab_get_chunk(slab_t *slab) {
167168
(void *)((uintptr_t)slab->mem_ptr + chunk_idx * slab->bucket->size);
168169

169170
// mark chunk as used
170-
slab->chunks[chunk_idx] = true;
171+
slab_set_chunk_bit(slab, chunk_idx, false);
171172
slab->num_chunks_allocated += 1;
172173

173-
// use the found index as the next hint
174-
slab->first_free_chunk_idx = chunk_idx + 1;
175-
176174
return free_chunk;
177175
}
178176

@@ -195,18 +193,9 @@ static void slab_free_chunk(slab_t *slab, void *ptr) {
195193
size_t chunk_idx = ptr_diff / slab->bucket->size;
196194

197195
// Make sure that the chunk was allocated
198-
assert(slab->chunks[chunk_idx] && "double free detected");
199-
slab->chunks[chunk_idx] = false;
196+
assert(slab_read_chunk_bit(slab, chunk_idx) == 0 && "double free detected");
197+
slab_set_chunk_bit(slab, chunk_idx, true);
200198
slab->num_chunks_allocated -= 1;
201-
202-
if (chunk_idx < slab->first_free_chunk_idx) {
203-
slab->first_free_chunk_idx = chunk_idx;
204-
}
205-
206-
LOG_DEBUG("chunk_idx: %zu, num_chunks_allocated: %zu, "
207-
"first_free_chunk_idx: %zu",
208-
chunk_idx, slab->num_chunks_allocated,
209-
slab->first_free_chunk_idx);
210199
}
211200

212201
static bool slab_has_avail(const slab_t *slab) {

src/pool/pool_disjoint_internal.h

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "critnib/critnib.h"
1616
#include "utils_concurrency.h"
1717

18+
#define CHUNK_BITMAP_SIZE 64
19+
1820
typedef struct bucket_t bucket_t;
1921
typedef struct slab_t slab_t;
2022
typedef struct slab_list_item_t slab_list_item_t;
@@ -81,23 +83,24 @@ typedef struct slab_t {
8183
void *mem_ptr;
8284
size_t slab_size;
8385

84-
// Represents the current state of each chunk: if the bit is set, the
85-
// chunk is allocated; otherwise, the chunk is free for allocation
86-
bool *chunks;
8786
size_t num_chunks_total;
8887

88+
// Num of 64-bit words needed to store chunk state
89+
size_t num_words;
90+
8991
// Total number of allocated chunks at the moment.
9092
size_t num_chunks_allocated;
9193

9294
// The bucket which the slab belongs to
9395
bucket_t *bucket;
9496

95-
// Hints where to start search for free chunk in a slab
96-
size_t first_free_chunk_idx;
97-
9897
// Store iterator to the corresponding node in avail/unavail list
9998
// to achieve O(1) removal
10099
slab_list_item_t iter;
100+
101+
// Represents the current state of each chunk: if the bit is clear, the
102+
// chunk is allocated; otherwise, the chunk is free for allocation
103+
uint64_t chunks[];
101104
} slab_t;
102105

103106
typedef struct umf_disjoint_pool_shared_limits_t {
@@ -158,4 +161,24 @@ typedef struct disjoint_pool_t {
158161
size_t provider_min_page_size;
159162
} disjoint_pool_t;
160163

164+
static inline void slab_set_chunk_bit(slab_t *slab, size_t index, bool value) {
165+
assert(index < slab->num_chunks_total && "Index out of range");
166+
167+
size_t word_index = index / CHUNK_BITMAP_SIZE;
168+
unsigned bit_index = index % CHUNK_BITMAP_SIZE;
169+
if (value) {
170+
slab->chunks[word_index] |= (1ULL << bit_index);
171+
} else {
172+
slab->chunks[word_index] &= ~(1ULL << bit_index);
173+
}
174+
}
175+
176+
static inline int slab_read_chunk_bit(const slab_t *slab, size_t index) {
177+
assert(index < slab->num_chunks_total && "Index out of range");
178+
179+
size_t word_index = index / CHUNK_BITMAP_SIZE;
180+
unsigned bit_index = index % CHUNK_BITMAP_SIZE;
181+
return (slab->chunks[word_index] >> bit_index) & 1;
182+
}
183+
161184
#endif // UMF_POOL_DISJOINT_INTERNAL_H

test/pools/disjoint_pool.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,8 @@ TEST_F(test, internals) {
113113
EXPECT_GE(slab->num_chunks_total, slab->slab_size / bucket->size);
114114

115115
// check allocation in slab
116-
EXPECT_EQ(slab->chunks[0], true);
117-
EXPECT_EQ(slab->chunks[1], false);
118-
EXPECT_EQ(slab->first_free_chunk_idx, 1);
116+
EXPECT_EQ(slab_read_chunk_bit(slab, 0), false);
117+
EXPECT_EQ(slab_read_chunk_bit(slab, 1), true);
119118

120119
// TODO:
121120
// * multiple alloc + free from single bucket

0 commit comments

Comments
 (0)