Skip to content

Commit f011d5b

Browse files
committed
prio-queue: fold lazy_queue into prio_queue for automatic get+put fusion
Defer the actual removal in prio_queue_get() until the next operation. If that next operation is a prio_queue_put(), the removal and insertion are fused into a single replace — writing the new element at the root and sifting it down — which avoids a full remove-rebalance-insert cycle. This matches the dominant usage pattern in git's commit traversal: get a commit, then put its parents. The first parent insertion after each get is now a replace operation automatically. This generalizes the lazy_queue pattern from builtin/describe.c (introduced in 08bb69d) into prio_queue itself. Three callers independently implemented the same get+put fusion: - builtin/describe.c had a full lazy_queue wrapper - commit.c:pop_most_recent_commit() reimplements the same get_pending flag with peek+replace - builtin/show-branch.c:join_revs() used the same peek+replace pattern All three now collapse to plain _get() and _put(), with the data structure handling the fusion internally. Remove prio_queue_replace() since no external callers remain. Add prio_queue_size() for callers that need the logical element count, since the physical nr may temporarily include a pending-removal element. Benchmarked on a large monorepo (10-15 interleaved runs, 1 warmup): Command base patched speedup merge-base --all A A~1000 3.88s 3.77s 1.03x rev-list --count A~1000..A 3.57s 3.43s 1.04x log --oneline A~1000..A 3.70s 3.49s 1.06x rev-parse :/pattern 365ms 364ms 1.00x describe HEAD (linux.git) 184ms 190ms 1.00x No regressions in any scenario. Suggested-by: René Scharfe <l.s.r@web.de> Signed-off-by: Kristofer Karlsson <krka@spotify.com>
1 parent 9ac3f19 commit f011d5b

11 files changed

Lines changed: 85 additions & 138 deletions

File tree

builtin/describe.c

Lines changed: 15 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -251,56 +251,20 @@ static int compare_pt(const void *a_, const void *b_)
251251
return 0;
252252
}
253253

254-
struct lazy_queue {
255-
struct prio_queue queue;
256-
bool get_pending;
257-
};
258-
259-
#define LAZY_QUEUE_INIT { { compare_commits_by_commit_date }, false }
260-
261-
static void *lazy_queue_get(struct lazy_queue *queue)
262-
{
263-
if (queue->get_pending)
264-
prio_queue_get(&queue->queue);
265-
else
266-
queue->get_pending = true;
267-
return prio_queue_peek(&queue->queue);
268-
}
269-
270-
static void lazy_queue_put(struct lazy_queue *queue, void *thing)
271-
{
272-
if (queue->get_pending)
273-
prio_queue_replace(&queue->queue, thing);
274-
else
275-
prio_queue_put(&queue->queue, thing);
276-
queue->get_pending = false;
277-
}
278-
279-
static bool lazy_queue_empty(const struct lazy_queue *queue)
280-
{
281-
return queue->queue.nr == (queue->get_pending ? 1 : 0);
282-
}
283-
284-
static void lazy_queue_clear(struct lazy_queue *queue)
285-
{
286-
clear_prio_queue(&queue->queue);
287-
queue->get_pending = false;
288-
}
289-
290-
static unsigned long finish_depth_computation(struct lazy_queue *queue,
254+
static unsigned long finish_depth_computation(struct prio_queue *queue,
291255
struct possible_tag *best)
292256
{
293257
unsigned long seen_commits = 0;
294258
struct oidset unflagged = OIDSET_INIT;
259+
struct commit *c;
295260

296-
for (size_t i = queue->get_pending ? 1 : 0; i < queue->queue.nr; i++) {
297-
struct commit *commit = queue->queue.array[i].data;
261+
for (size_t i = queue->get_pending; i < queue->nr; i++) {
262+
struct commit *commit = queue->array[i].data;
298263
if (!(commit->object.flags & best->flag_within))
299264
oidset_insert(&unflagged, &commit->object.oid);
300265
}
301266

302-
while (!lazy_queue_empty(queue)) {
303-
struct commit *c = lazy_queue_get(queue);
267+
while ((c = prio_queue_get(queue))) {
304268
struct commit_list *parents = c->parents;
305269
seen_commits++;
306270
if (c->object.flags & best->flag_within) {
@@ -316,7 +280,7 @@ static unsigned long finish_depth_computation(struct lazy_queue *queue,
316280
repo_parse_commit(the_repository, p);
317281
seen = p->object.flags & SEEN;
318282
if (!seen)
319-
lazy_queue_put(queue, p);
283+
prio_queue_put(queue, p);
320284
flag_before = p->object.flags & best->flag_within;
321285
p->object.flags |= c->object.flags;
322286
flag_after = p->object.flags & best->flag_within;
@@ -364,8 +328,8 @@ static void append_suffix(int depth, const struct object_id *oid, struct strbuf
364328

365329
static void describe_commit(struct commit *cmit, struct strbuf *dst)
366330
{
367-
struct commit *gave_up_on = NULL;
368-
struct lazy_queue queue = LAZY_QUEUE_INIT;
331+
struct commit *c, *gave_up_on = NULL;
332+
struct prio_queue queue = { compare_commits_by_commit_date };
369333
struct commit_name *n;
370334
struct possible_tag all_matches[MAX_TAGS];
371335
unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
@@ -407,9 +371,8 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst)
407371
}
408372

409373
cmit->object.flags = SEEN;
410-
lazy_queue_put(&queue, cmit);
411-
while (!lazy_queue_empty(&queue)) {
412-
struct commit *c = lazy_queue_get(&queue);
374+
prio_queue_put(&queue, cmit);
375+
while ((c = prio_queue_get(&queue))) {
413376
struct commit_list *parents = c->parents;
414377
struct commit_name **slot;
415378

@@ -443,7 +406,7 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst)
443406
t->depth++;
444407
}
445408
/* Stop if last remaining path already covered by best candidate(s) */
446-
if (annotated_cnt && lazy_queue_empty(&queue)) {
409+
if (annotated_cnt && !prio_queue_size(&queue)) {
447410
int best_depth = INT_MAX;
448411
unsigned best_within = 0;
449412
for (cur_match = 0; cur_match < match_cnt; cur_match++) {
@@ -466,7 +429,7 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst)
466429
struct commit *p = parents->item;
467430
repo_parse_commit(the_repository, p);
468431
if (!(p->object.flags & SEEN))
469-
lazy_queue_put(&queue, p);
432+
prio_queue_put(&queue, p);
470433
p->object.flags |= c->object.flags;
471434
parents = parents->next;
472435

@@ -481,7 +444,7 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst)
481444
strbuf_add_unique_abbrev(dst, cmit_oid, abbrev);
482445
if (suffix)
483446
strbuf_addstr(dst, suffix);
484-
lazy_queue_clear(&queue);
447+
clear_prio_queue(&queue);
485448
return;
486449
}
487450
if (unannotated_cnt)
@@ -497,11 +460,11 @@ static void describe_commit(struct commit *cmit, struct strbuf *dst)
497460
QSORT(all_matches, match_cnt, compare_pt);
498461

499462
if (gave_up_on) {
500-
lazy_queue_put(&queue, gave_up_on);
463+
prio_queue_put(&queue, gave_up_on);
501464
seen_commits--;
502465
}
503466
seen_commits += finish_depth_computation(&queue, &all_matches[0]);
504-
lazy_queue_clear(&queue);
467+
clear_prio_queue(&queue);
505468

506469
if (debug) {
507470
static int label_width = -1;

builtin/last-modified.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ static void process_parent(struct last_modified *lm,
344344
static int last_modified_run(struct last_modified *lm)
345345
{
346346
int max_count, queue_popped = 0;
347+
struct commit *c;
347348
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
348349
struct prio_queue not_queue = { compare_commits_by_gen_then_commit_date };
349350
struct commit_list *list;
@@ -389,10 +390,9 @@ static int last_modified_run(struct last_modified *lm)
389390
}
390391
}
391392

392-
while (queue.nr) {
393+
while ((c = prio_queue_get(&queue))) {
393394
int parent_i;
394395
struct commit_list *p;
395-
struct commit *c = prio_queue_get(&queue);
396396
struct bitmap *active_c = active_paths_for(lm, c);
397397

398398
if ((0 <= max_count && max_count < ++queue_popped) ||

builtin/show-branch.c

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ static const char *get_color_reset_code(void)
6262

6363
static struct commit *interesting(struct prio_queue *queue)
6464
{
65-
for (size_t i = 0; i < queue->nr; i++) {
65+
for (size_t i = queue->get_pending; i < queue->nr; i++) {
6666
struct commit *commit = queue->array[i].data;
6767
if (commit->object.flags & UNINTERESTING)
6868
continue;
@@ -228,17 +228,18 @@ static void join_revs(struct prio_queue *queue,
228228
{
229229
int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
230230
int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
231+
struct commit *commit;
231232

232-
while (queue->nr) {
233+
while ((commit = prio_queue_peek(queue))) {
233234
struct commit_list *parents;
234235
int still_interesting = !!interesting(queue);
235-
struct commit *commit = prio_queue_peek(queue);
236-
bool get_pending = true;
237236
int flags = commit->object.flags & all_mask;
238237

239238
if (!still_interesting && extra <= 0)
240239
break;
241240

241+
prio_queue_get(queue);
242+
242243
mark_seen(commit, seen_p);
243244
if ((flags & all_revs) == all_revs)
244245
flags |= UNINTERESTING;
@@ -254,14 +255,8 @@ static void join_revs(struct prio_queue *queue,
254255
if (mark_seen(p, seen_p) && !still_interesting)
255256
extra--;
256257
p->object.flags |= flags;
257-
if (get_pending)
258-
prio_queue_replace(queue, p);
259-
else
260-
prio_queue_put(queue, p);
261-
get_pending = false;
258+
prio_queue_put(queue, p);
262259
}
263-
if (get_pending)
264-
prio_queue_get(queue);
265260
}
266261

267262
/*

commit-reach.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,7 @@ int get_branch_base_for_tip(struct repository *r,
12691269
size_t bases_nr)
12701270
{
12711271
int best_index = -1;
1272-
struct commit *branch_point = NULL;
1272+
struct commit *c, *branch_point = NULL;
12731273
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
12741274
int found_missing_gen = 0;
12751275

@@ -1322,8 +1322,7 @@ int get_branch_base_for_tip(struct repository *r,
13221322
prio_queue_put(&queue, c);
13231323
}
13241324

1325-
while (queue.nr) {
1326-
struct commit *c = prio_queue_get(&queue);
1325+
while ((c = prio_queue_get(&queue))) {
13271326
int best_for_c = get_best(c);
13281327
int best_for_p, positive;
13291328
struct commit *parent;

commit.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -795,24 +795,17 @@ void commit_list_sort_by_date(struct commit_list **list)
795795
struct commit *pop_most_recent_commit(struct prio_queue *queue,
796796
unsigned int mark)
797797
{
798-
struct commit *ret = prio_queue_peek(queue);
799-
int get_pending = 1;
798+
struct commit *ret = prio_queue_get(queue);
800799
struct commit_list *parents = ret->parents;
801800

802801
while (parents) {
803802
struct commit *commit = parents->item;
804803
if (!repo_parse_commit(the_repository, commit) && !(commit->object.flags & mark)) {
805804
commit->object.flags |= mark;
806-
if (get_pending)
807-
prio_queue_replace(queue, commit);
808-
else
809-
prio_queue_put(queue, commit);
810-
get_pending = 0;
805+
prio_queue_put(queue, commit);
811806
}
812807
parents = parents->next;
813808
}
814-
if (get_pending)
815-
prio_queue_get(queue);
816809
return ret;
817810
}
818811

pack-bitmap-write.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,16 +513,16 @@ static int fill_bitmap_commit(struct bitmap_writer *writer,
513513
struct bitmap_index *old_bitmap,
514514
const uint32_t *mapping)
515515
{
516+
struct commit *c;
516517
int found;
517518
uint32_t pos;
518519
if (!ent->bitmap)
519520
ent->bitmap = bitmap_new();
520521

521522
prio_queue_put(queue, commit);
522523

523-
while (queue->nr) {
524+
while ((c = prio_queue_get(queue))) {
524525
struct commit_list *p;
525-
struct commit *c = prio_queue_get(queue);
526526

527527
if (old_bitmap && mapping) {
528528
struct ewah_bitmap *old;

prio-queue.c

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -34,64 +34,69 @@ void clear_prio_queue(struct prio_queue *queue)
3434
queue->nr = 0;
3535
queue->alloc = 0;
3636
queue->insertion_ctr = 0;
37+
queue->get_pending = 0;
38+
}
39+
40+
static void sift_down_root(struct prio_queue *queue)
41+
{
42+
size_t ix, child;
43+
44+
for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
45+
child = ix * 2 + 1;
46+
if (child + 1 < queue->nr &&
47+
compare(queue, child, child + 1) >= 0)
48+
child++;
49+
if (compare(queue, ix, child) <= 0)
50+
break;
51+
swap(queue, child, ix);
52+
}
3753
}
3854

3955
void prio_queue_put(struct prio_queue *queue, void *thing)
4056
{
4157
size_t ix, parent;
4258

43-
/* Append at the end */
59+
if (queue->get_pending) {
60+
queue->get_pending = 0;
61+
queue->array[0].ctr = queue->insertion_ctr++;
62+
queue->array[0].data = thing;
63+
sift_down_root(queue);
64+
return;
65+
}
66+
4467
ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc);
4568
queue->array[queue->nr].ctr = queue->insertion_ctr++;
4669
queue->array[queue->nr].data = thing;
4770
queue->nr++;
4871
if (!queue->compare)
49-
return; /* LIFO */
72+
return;
5073

51-
/* Bubble up the new one */
5274
for (ix = queue->nr - 1; ix; ix = parent) {
5375
parent = (ix - 1) / 2;
5476
if (compare(queue, parent, ix) <= 0)
5577
break;
56-
5778
swap(queue, parent, ix);
5879
}
5980
}
6081

61-
static void sift_down_root(struct prio_queue *queue)
62-
{
63-
size_t ix, child;
64-
65-
/* Push down the one at the root */
66-
for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
67-
child = ix * 2 + 1; /* left */
68-
if (child + 1 < queue->nr &&
69-
compare(queue, child, child + 1) >= 0)
70-
child++; /* use right child */
71-
72-
if (compare(queue, ix, child) <= 0)
73-
break;
74-
75-
swap(queue, child, ix);
76-
}
77-
}
78-
7982
void *prio_queue_get(struct prio_queue *queue)
8083
{
81-
void *result;
82-
8384
if (!queue->nr)
8485
return NULL;
8586
if (!queue->compare)
86-
return queue->array[--queue->nr].data; /* LIFO */
87-
88-
result = queue->array[0].data;
89-
if (!--queue->nr)
90-
return result;
87+
return queue->array[--queue->nr].data;
88+
89+
if (queue->get_pending) {
90+
if (!--queue->nr) {
91+
queue->get_pending = 0;
92+
return NULL;
93+
}
94+
queue->array[0] = queue->array[queue->nr];
95+
sift_down_root(queue);
96+
}
9197

92-
queue->array[0] = queue->array[queue->nr];
93-
sift_down_root(queue);
94-
return result;
98+
queue->get_pending = 1;
99+
return queue->array[0].data;
95100
}
96101

97102
void *prio_queue_peek(struct prio_queue *queue)
@@ -100,19 +105,14 @@ void *prio_queue_peek(struct prio_queue *queue)
100105
return NULL;
101106
if (!queue->compare)
102107
return queue->array[queue->nr - 1].data;
103-
return queue->array[0].data;
104-
}
105108

106-
void prio_queue_replace(struct prio_queue *queue, void *thing)
107-
{
108-
if (!queue->nr) {
109-
prio_queue_put(queue, thing);
110-
} else if (!queue->compare) {
111-
queue->array[queue->nr - 1].ctr = queue->insertion_ctr++;
112-
queue->array[queue->nr - 1].data = thing;
113-
} else {
114-
queue->array[0].ctr = queue->insertion_ctr++;
115-
queue->array[0].data = thing;
109+
if (queue->get_pending) {
110+
queue->get_pending = 0;
111+
if (!--queue->nr)
112+
return NULL;
113+
queue->array[0] = queue->array[queue->nr];
116114
sift_down_root(queue);
117115
}
116+
117+
return queue->array[0].data;
118118
}

0 commit comments

Comments
 (0)