Skip to content
This repository was archived by the owner on Aug 19, 2021. It is now read-only.

Commit e638d20

Browse files
committed
Added slotting for sibling events in queue
The queue data-structure has been molded into a 2d double linked-list with slots for each set of events that expire at a given time. Other than having little downsides, the biggest benifit is a constant-time enqueueing of events with no delay while still maintaining ordering. To reduce the memory overhead for each event, the queue is not doubly-linked in both directions. Instead, noting that no more than 1 reference is needed for each event, the queue is stored as a 2d single linked-list with generalized pointers to whatever references events. To maintain constant-enqueueing without a tail pointer for each slot, events are inserted as a stack and reversed on dequeueing. This does not impact amortized complexity as each event already has to be marked dirty. Notable performance impact (make prof): equeue_post_many_prof: 202 cycles (+98%) equeue_post_future_many_prof: 207 cycles (+98%) equeue_alloc_size_prof: 56 bytes (-16%) equeue_alloc_many_size_prof: 64000 bytes (-14%) equeue_alloc_fragmented_size_prof: 64000 bytes (-14%)
1 parent c4d4839 commit e638d20

File tree

2 files changed

+78
-54
lines changed

2 files changed

+78
-54
lines changed

equeue.c

Lines changed: 76 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -76,59 +76,57 @@ static struct equeue_event *equeue_mem_alloc(equeue_t *q, size_t size) {
7676

7777
equeue_mutex_lock(&q->memlock);
7878

79-
for (struct equeue_chunk **p = &q->chunks; *p; p = &(*p)->nchunk) {
79+
for (struct equeue_event **p = &q->chunks; *p; p = &(*p)->next) {
8080
if ((*p)->size >= size) {
81-
struct equeue_chunk *c = *p;
82-
if (c->next) {
83-
*p = c->next;
84-
(*p)->nchunk = c->nchunk;
81+
struct equeue_event *e = *p;
82+
if (e->sibling) {
83+
*p = e->sibling;
84+
(*p)->next = e->next;
8585
} else {
86-
*p = c->nchunk;
86+
*p = e->next;
8787
}
8888

89-
c->id += 1;
90-
if (c->id >> (8*sizeof(int)-1 - q->npw2)) {
91-
c->id = 1;
89+
e->id += 1;
90+
if (e->id >> (8*sizeof(int)-1 - q->npw2)) {
91+
e->id = 1;
9292
}
9393

9494
equeue_mutex_unlock(&q->memlock);
95-
return (struct equeue_event *)c;
95+
return e;
9696
}
9797
}
9898

9999
if (q->slab.size >= size) {
100-
struct equeue_chunk *c = (struct equeue_chunk *)q->slab.data;
100+
struct equeue_event *e = (struct equeue_event *)q->slab.data;
101101
q->slab.data += size;
102102
q->slab.size -= size;
103-
c->size = size;
104-
c->id = 1;
103+
e->size = size;
104+
e->id = 1;
105105

106106
equeue_mutex_unlock(&q->memlock);
107-
return (struct equeue_event *)c;
107+
return e;
108108
}
109109

110110
equeue_mutex_unlock(&q->memlock);
111111
return 0;
112112
}
113113

114114
static void equeue_mem_dealloc(equeue_t *q, struct equeue_event *e) {
115-
struct equeue_chunk *c = (struct equeue_chunk *)e;
116-
117115
equeue_mutex_lock(&q->memlock);
118116

119-
struct equeue_chunk **p = &q->chunks;
120-
while (*p && (*p)->size < c->size) {
121-
p = &(*p)->nchunk;
117+
struct equeue_event **p = &q->chunks;
118+
while (*p && (*p)->size < e->size) {
119+
p = &(*p)->next;
122120
}
123121

124-
if (*p && (*p)->size == c->size) {
125-
c->next = *p;
126-
c->nchunk = (*p)->nchunk;
122+
if (*p && (*p)->size == e->size) {
123+
e->sibling = *p;
124+
e->next = (*p)->next;
127125
} else {
128-
c->next = 0;
129-
c->nchunk = *p;
126+
e->sibling = 0;
127+
e->next = *p;
130128
}
131-
*p = c;
129+
*p = e;
132130

133131
equeue_mutex_unlock(&q->memlock);
134132
}
@@ -166,48 +164,78 @@ static void equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned ms) {
166164
e->target = equeue_tick() + ms;
167165

168166
struct equeue_event **p = &q->queue;
169-
while (*p && equeue_tickdiff((*p)->target, e->target) <= 0) {
167+
while (*p && equeue_tickdiff((*p)->target, e->target) < 0) {
170168
p = &(*p)->next;
171169
}
172170

173-
e->ref = p;
174-
e->next = *p;
175-
if (*p) {
176-
(*p)->ref = &e->next;
171+
if (*p && (*p)->target == e->target) {
172+
if (*p) {
173+
(*p)->ref = &e->sibling;
174+
}
175+
e->sibling = *p;
176+
177+
if ((*p)->next) {
178+
(*p)->next->ref = &e->next;
179+
}
180+
e->next = (*p)->next;
181+
} else {
182+
if (*p) {
183+
(*p)->ref = &e->next;
184+
}
185+
e->next = *p;
186+
187+
e->sibling = 0;
177188
}
189+
190+
e->ref = p;
178191
*p = e;
179192
}
180193

181194
static void equeue_unqueue(equeue_t *q, struct equeue_event *e) {
182-
if (e->next) {
183-
e->next->ref = e->ref;
195+
if (e->sibling) {
196+
if (e->next) {
197+
e->next->ref = &e->sibling->next;
198+
}
199+
e->sibling->next = e->next;
200+
201+
e->sibling->ref = e->ref;
202+
*e->ref = e->sibling;
203+
} else {
204+
if (e->next) {
205+
e->next->ref = e->ref;
206+
}
207+
*e->ref = e->next;
184208
}
185-
*e->ref = e->next;
186209
}
187210

188-
static struct equeue_event *equeue_dequeue(
189-
equeue_t *q, unsigned target, int *deadline) {
190-
struct equeue_event *head = q->queue;
191-
if (!head || equeue_tickdiff(head->target, target) > 0) {
192-
return 0;
193-
}
211+
static struct equeue_event *equeue_dequeue(equeue_t *q, int *deadline) {
212+
unsigned target = equeue_tick();
213+
struct equeue_event *head = 0;
214+
struct equeue_event **tail = &head;
194215

195-
struct equeue_event **p = &q->queue;
196-
while (*p) {
197-
int diff = equeue_tickdiff((*p)->target, target);
216+
while (q->queue) {
217+
int diff = equeue_tickdiff(q->queue->target, target);
198218
if (diff > 0) {
199219
*deadline = diff;
200220
break;
201221
}
202222

203-
p = &(*p)->next;
223+
struct equeue_event *es = q->queue;
224+
q->queue = es->next;
225+
226+
struct equeue_event *prev = 0;
227+
for (struct equeue_event *e = es; e; e = e->sibling) {
228+
e->next = prev;
229+
prev = e;
230+
}
231+
232+
*tail = prev;
233+
tail = &es->next;
204234
}
205235

206-
if (*p) {
207-
(*p)->ref = &q->queue;
236+
if (q->queue) {
237+
q->queue->ref = &q->queue;
208238
}
209-
q->queue = *p;
210-
*p = 0;
211239

212240
return head;
213241
}
@@ -267,7 +295,7 @@ void equeue_dispatch(equeue_t *q, int ms) {
267295
int deadline = -1;
268296
if (q->queue) {
269297
equeue_mutex_lock(&q->queuelock);
270-
es = equeue_dequeue(q, equeue_tick(), &deadline);
298+
es = equeue_dequeue(q, &deadline);
271299

272300
// mark events as in-flight
273301
for (struct equeue_event *e = es; e; e = e->next) {

equeue.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct equeue_event {
2828
unsigned size;
2929
int id;
3030
struct equeue_event *next;
31+
struct equeue_event *sibling;
3132
struct equeue_event **ref;
3233

3334
unsigned target;
@@ -46,12 +47,7 @@ typedef struct equeue {
4647
unsigned npw2;
4748
void *allocated;
4849

49-
struct equeue_chunk {
50-
unsigned size;
51-
int id;
52-
struct equeue_chunk *next;
53-
struct equeue_chunk *nchunk;
54-
} *chunks;
50+
struct equeue_event *chunks;
5551
struct equeue_slab {
5652
size_t size;
5753
unsigned char *data;

0 commit comments

Comments
 (0)