Skip to content

Commit 6fa1e44

Browse files
committed
askrene: add a queue for primitive data types
Changelog-None Signed-off-by: Lagrang3 <[email protected]>
1 parent 83a88ee commit 6fa1e44

File tree

3 files changed

+187
-0
lines changed

3 files changed

+187
-0
lines changed

plugins/askrene/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ PLUGIN_ASKRENE_HEADER := \
2222
plugins/askrene/explain_failure.h \
2323
plugins/askrene/graph.h \
2424
plugins/askrene/priorityqueue.h \
25+
plugins/askrene/queue.h \
2526
plugins/askrene/algorithm.h
2627

2728
PLUGIN_ASKRENE_OBJS := $(PLUGIN_ASKRENE_SRC:.c=.o)

plugins/askrene/queue.h

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#ifndef LIGHTNING_PLUGINS_ASKRENE_QUEUE_H
2+
#define LIGHTNING_PLUGINS_ASKRENE_QUEUE_H
3+
4+
#include "config.h"
5+
#include <ccan/compiler/compiler.h>
6+
#include <ccan/lqueue/lqueue.h>
7+
#include <ccan/tal/tal.h>
8+
9+
/* Generic and efficient queue based on ccan/lqueue for primitive data.
10+
* The size of the cache of 64 is the smallest power of two for which I obtain a
11+
* significant time improvement over directly using lqueue, ie. one lqueue
12+
* element for each item in the queue. For a small problem sizes (~10) the
13+
* speed-up is 3x, for large problem sizes
14+
* (>1000) the speed-up is 7x.
15+
* ~0.5 operations/nsec */
16+
17+
#define QUEUE_CACHE_SIZE 64
18+
19+
#define QUEUE_DEFINE_TYPE(type, name) \
20+
struct name##_qcache_ { \
21+
struct lqueue_link qlink; \
22+
int begin, end; \
23+
type data[QUEUE_CACHE_SIZE]; \
24+
}; \
25+
static inline UNNEEDED bool name##_qcache_empty_( \
26+
const struct name##_qcache_ *qc) \
27+
{ \
28+
return qc->begin == qc->end; \
29+
} \
30+
/* UB if _qcache is empty */ \
31+
static inline UNNEEDED type name##_qcache_front_( \
32+
const struct name##_qcache_ *qc) \
33+
{ \
34+
return qc->data[qc->begin]; \
35+
} \
36+
static inline UNNEEDED type name##_qcache_pop_( \
37+
struct name##_qcache_ *qc) \
38+
{ \
39+
type r = name##_qcache_front_(qc); \
40+
qc->begin++; \
41+
if (qc->begin >= qc->end) { \
42+
qc->begin = qc->end = 0; \
43+
} \
44+
return r; \
45+
} \
46+
static inline UNNEEDED bool name##_qcache_insert_( \
47+
struct name##_qcache_ *qc, type element) \
48+
{ \
49+
if (qc->end == QUEUE_CACHE_SIZE) { \
50+
return false; \
51+
} \
52+
qc->data[qc->end++] = element; \
53+
return true; \
54+
} \
55+
static inline UNNEEDED void name##_qcache_init_( \
56+
struct name##_qcache_ *qc) \
57+
{ \
58+
qc->begin = qc->end = 0; \
59+
} \
60+
struct name { \
61+
const tal_t *ctx; \
62+
struct lqueue_ lq; \
63+
}; \
64+
static inline UNNEEDED bool name##_empty(const struct name *q) \
65+
{ \
66+
return lqueue_empty_(&q->lq); \
67+
} \
68+
static inline UNNEEDED type name##_front(const struct name *q) \
69+
{ \
70+
type r; \
71+
const struct name##_qcache_ *qc = \
72+
(const struct name##_qcache_ *)lqueue_front_(&q->lq); \
73+
if (qc) \
74+
r = name##_qcache_front_(qc); \
75+
return r; \
76+
} \
77+
static inline UNNEEDED type name##_pop(struct name *q) \
78+
{ \
79+
type r; \
80+
struct name##_qcache_ *qc = \
81+
(struct name##_qcache_ *)lqueue_front_(&q->lq); \
82+
if (qc) \
83+
r = name##_qcache_pop_(qc); \
84+
else \
85+
memset(&r, 0, sizeof(type)); \
86+
if (qc && name##_qcache_empty_(qc)) { \
87+
lqueue_dequeue_(&q->lq); \
88+
tal_free(qc); \
89+
} \
90+
return r; \
91+
} \
92+
static inline UNNEEDED void name##_init(struct name *q, \
93+
const tal_t *ctx) \
94+
{ \
95+
q->ctx = ctx; \
96+
lqueue_init_(&q->lq, NULL); \
97+
} \
98+
static inline UNNEEDED void name##_insert(struct name *q, \
99+
type element) \
100+
{ \
101+
struct name##_qcache_ *qc = \
102+
(struct name##_qcache_ *)lqueue_back_(&q->lq); \
103+
if (qc && name##_qcache_insert_(qc, element)) \
104+
return; \
105+
qc = tal(q->ctx, struct name##_qcache_); \
106+
name##_qcache_init_(qc); \
107+
name##_qcache_insert_(qc, element); \
108+
lqueue_enqueue_(&q->lq, (struct lqueue_link *)qc); \
109+
} \
110+
/* QUEUE_DEFINE_TYPE */
111+
112+
#endif /* LIGHTNING_PLUGINS_ASKRENE_QUEUE_H */

plugins/askrene/test/run-queue.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include "config.h"
2+
#include "../queue.h"
3+
#include <stdlib.h>
4+
5+
/* a queue for int */
6+
QUEUE_DEFINE_TYPE(int, iqueue);
7+
8+
int main(int argc, char *argv[])
9+
{
10+
int x;
11+
struct iqueue q;
12+
iqueue_init(&q, NULL);
13+
14+
iqueue_insert(&q, 1);
15+
x = iqueue_pop(&q);
16+
assert(x == 1);
17+
18+
iqueue_insert(&q, 2);
19+
x = iqueue_pop(&q);
20+
assert(x == 2);
21+
22+
iqueue_insert(&q, 3);
23+
iqueue_insert(&q, 4);
24+
x = iqueue_pop(&q);
25+
assert(x == 3);
26+
x = iqueue_pop(&q);
27+
assert(x == 4);
28+
29+
iqueue_insert(&q, 5);
30+
iqueue_insert(&q, 6);
31+
x = iqueue_pop(&q);
32+
assert(x == 5);
33+
iqueue_insert(&q, 7);
34+
x = iqueue_pop(&q);
35+
assert(x == 6);
36+
x = iqueue_pop(&q);
37+
assert(x == 7);
38+
39+
for (int i = 1; i <= 10000; i++)
40+
iqueue_insert(&q, i);
41+
for (int i = 1; i <= 10000; i++) {
42+
x = iqueue_pop(&q);
43+
assert(x == i);
44+
}
45+
46+
const int MAX_ITEM = 1000000;
47+
int expected_front = 1, next_insert = 1;
48+
49+
do {
50+
if (iqueue_empty(&q) && next_insert > MAX_ITEM)
51+
break;
52+
53+
if (iqueue_empty(&q)) {
54+
/* we can only insert */
55+
iqueue_insert(&q, next_insert++);
56+
} else if (next_insert > MAX_ITEM) {
57+
/* we can only pop */
58+
x = iqueue_pop(&q);
59+
assert(x == expected_front);
60+
expected_front++;
61+
} else {
62+
/* we can both insert and pop, throw a coin */
63+
if (rand() % 2) {
64+
iqueue_insert(&q, next_insert++);
65+
} else {
66+
x = iqueue_pop(&q);
67+
assert(x == expected_front);
68+
expected_front++;
69+
}
70+
}
71+
} while (1);
72+
73+
return 0;
74+
}

0 commit comments

Comments
 (0)