mirror of
https://github.com/git-for-windows/git.git
synced 2026-06-14 15:01:15 -05:00
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 08bb69d70f) 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() used peek+replace
- builtin/show-branch.c:join_revs() used peek+replace
All three now collapse to plain _get() and _put(), with the data
structure handling the fusion internally. This simplifies callers
and means every prio_queue user gets the optimization for free
without needing to implement it manually.
Remove prio_queue_replace() since no external callers remain.
Benchmarked on a 1.8M-commit monorepo (30 interleaved runs,
paired t-test, Xeon @ 2.20GHz):
Code paths that previously did eager get+put (new optimization):
Command base patched change p
merge-base --all A A~1000 3828ms 3725ms -2.69% 0.0001
rev-list --count A~1000..A 3055ms 2986ms -2.27% 0.0601
log --oneline A~1000..A 3408ms 3350ms -1.71% 0.0482
Code paths that already had manual get+put fusion (expect
neutral — the optimization moves into prio_queue but the number
of heap operations stays the same):
Command base patched change p
show-branch A A~1000 9156ms 9127ms -0.32% 0.3470
describe (4751 revs, 81K repo) 1983ms 1963ms -1.02% <0.001
No regressions in any scenario.
Suggested-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
72 lines
2.0 KiB
C
72 lines
2.0 KiB
C
#ifndef PRIO_QUEUE_H
|
|
#define PRIO_QUEUE_H
|
|
|
|
/*
|
|
* A priority queue implementation, primarily for keeping track of
|
|
* commits in the 'date-order' so that we process them from new to old
|
|
* as they are discovered, but can be used to hold any pointer to
|
|
* struct. The caller is responsible for supplying a function to
|
|
* compare two "things".
|
|
*
|
|
* Alternatively, this data structure can also be used as a LIFO stack
|
|
* by specifying NULL as the comparison function.
|
|
*/
|
|
|
|
/*
|
|
* Compare two "things", one and two; the third parameter is cb_data
|
|
* in the prio_queue structure. The result is returned as a sign of
|
|
* the return value, being the same as the sign of the result of
|
|
* subtracting "two" from "one" (i.e. negative if "one" sorts earlier
|
|
* than "two").
|
|
*/
|
|
typedef int (*prio_queue_compare_fn)(const void *one, const void *two, void *cb_data);
|
|
|
|
struct prio_queue_entry {
|
|
size_t ctr;
|
|
void *data;
|
|
};
|
|
|
|
struct prio_queue {
|
|
prio_queue_compare_fn compare;
|
|
size_t insertion_ctr;
|
|
void *cb_data;
|
|
size_t alloc, nr_; /* use prio_queue_size() for logical count */
|
|
struct prio_queue_entry *array;
|
|
unsigned get_pending;
|
|
};
|
|
|
|
/*
|
|
* Add the "thing" to the queue.
|
|
*/
|
|
void prio_queue_put(struct prio_queue *, void *thing);
|
|
|
|
/*
|
|
* Extract the "thing" that compares the smallest out of the queue,
|
|
* or NULL. If compare function is NULL, the queue acts as a LIFO
|
|
* stack.
|
|
*/
|
|
void *prio_queue_get(struct prio_queue *);
|
|
|
|
/*
|
|
* Gain access to the "thing" that would be returned by
|
|
* prio_queue_get, but do not remove it from the queue.
|
|
*/
|
|
void *prio_queue_peek(struct prio_queue *);
|
|
|
|
static inline size_t prio_queue_size(const struct prio_queue *queue)
|
|
{
|
|
return queue->nr_ - queue->get_pending;
|
|
}
|
|
|
|
#define prio_queue_for_each(queue, it) \
|
|
for (size_t pq_ix_ = (queue)->get_pending; \
|
|
pq_ix_ < (queue)->nr_ && ((it) = (queue)->array[pq_ix_].data, 1); \
|
|
pq_ix_++)
|
|
|
|
void clear_prio_queue(struct prio_queue *);
|
|
|
|
/* Reverse the LIFO elements */
|
|
void prio_queue_reverse(struct prio_queue *);
|
|
|
|
#endif /* PRIO_QUEUE_H */
|