Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
prio-queue: make output stable with respect to insertion
If two items are added to a prio_queue and compare equal,
they currently come out in an apparently random order (this
order is deterministic for a particular sequence of
insertions and removals, but does not necessarily match the
insertion order). This makes it unlike using a date-ordered
commit_list, which is one of the main types we would like to
replace with it (because prio_queue does not suffer from
O(n) insertions).

We can make the priority queue stable by keeping an
insertion counter for each element, and using it to break
ties. This does increase the memory usage of the structure
(one int per element), but in practice it does not seem to
affect runtime. A best-of-five "git rev-list --topo-order"
on linux.git showed less than 1% difference (well within the
run-to-run noise).

In an ideal world, we would offer both stable and unstable
priority queues (the latter to try to maximize performance).
However, given the lack of a measurable performance
difference, it is not worth the extra code.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Jeff King authored and Junio C Hamano committed Jul 15, 2014
1 parent 6d63baa commit e8f91e3
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 6 deletions.
15 changes: 10 additions & 5 deletions prio-queue.c
Expand Up @@ -3,14 +3,16 @@

static inline int compare(struct prio_queue *queue, int i, int j)
{
int cmp = queue->compare(queue->array[i], queue->array[j],
int cmp = queue->compare(queue->array[i].data, queue->array[j].data,
queue->cb_data);
if (!cmp)
cmp = queue->array[i].ctr - queue->array[j].ctr;
return cmp;
}

static inline void swap(struct prio_queue *queue, int i, int j)
{
void *tmp = queue->array[i];
struct prio_queue_entry tmp = queue->array[i];
queue->array[i] = queue->array[j];
queue->array[j] = tmp;
}
Expand All @@ -31,6 +33,7 @@ void clear_prio_queue(struct prio_queue *queue)
queue->nr = 0;
queue->alloc = 0;
queue->array = NULL;
queue->insertion_ctr = 0;
}

void prio_queue_put(struct prio_queue *queue, void *thing)
Expand All @@ -39,7 +42,9 @@ void prio_queue_put(struct prio_queue *queue, void *thing)

/* Append at the end */
ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc);
queue->array[queue->nr++] = thing;
queue->array[queue->nr].ctr = queue->insertion_ctr++;
queue->array[queue->nr].data = thing;
queue->nr++;
if (!queue->compare)
return; /* LIFO */

Expand All @@ -61,9 +66,9 @@ void *prio_queue_get(struct prio_queue *queue)
if (!queue->nr)
return NULL;
if (!queue->compare)
return queue->array[--queue->nr]; /* LIFO */
return queue->array[--queue->nr].data; /* LIFO */

result = queue->array[0];
result = queue->array[0].data;
if (!--queue->nr)
return result;

Expand Down
8 changes: 7 additions & 1 deletion prio-queue.h
Expand Up @@ -21,11 +21,17 @@
*/
typedef int (*prio_queue_compare_fn)(const void *one, const void *two, void *cb_data);

struct prio_queue_entry {
unsigned ctr;
void *data;
};

struct prio_queue {
prio_queue_compare_fn compare;
unsigned insertion_ctr;
void *cb_data;
int alloc, nr;
void **array;
struct prio_queue_entry *array;
};

/*
Expand Down

0 comments on commit e8f91e3

Please sign in to comment.