Skip to content

Commit b48995f

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

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: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
r = name##_qcache_front_(qc); \
74+
return r; \
75+
} \
76+
static inline UNNEEDED type name##_pop(struct name *q) \
77+
{ \
78+
type r; \
79+
struct name##_qcache_ *qc = \
80+
(struct name##_qcache_ *)lqueue_front_(&q->lq); \
81+
r = name##_qcache_pop_(qc); \
82+
if (qc && name##_qcache_empty_(qc)) { \
83+
lqueue_dequeue_(&q->lq); \
84+
tal_free(qc); \
85+
} \
86+
return r; \
87+
} \
88+
static inline UNNEEDED void name##_init(struct name *q, \
89+
const tal_t *ctx) \
90+
{ \
91+
q->ctx = ctx; \
92+
lqueue_init_(&q->lq, NULL); \
93+
} \
94+
static inline UNNEEDED void name##_insert(struct name *q, \
95+
type element) \
96+
{ \
97+
struct name##_qcache_ *qc = \
98+
(struct name##_qcache_ *)lqueue_back_(&q->lq); \
99+
if (qc && name##_qcache_insert_(qc, element)) \
100+
return; \
101+
qc = tal(q->ctx, struct name##_qcache_); \
102+
name##_qcache_init_(qc); \
103+
name##_qcache_insert_(qc, element); \
104+
lqueue_enqueue_(&q->lq, (struct lqueue_link *)qc); \
105+
} \
106+
/* QUEUE_DEFINE_TYPE */
107+
108+
#endif /* LIGHTNING_PLUGINS_ASKRENE_QUEUE_H */

plugins/askrene/test/run-queue.c

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

0 commit comments

Comments
 (0)