From: Jeff King <peff@peff.net>
To: Junio C Hamano <gitster@pobox.com>
Cc: git@vger.kernel.org, Thomas Rast <trast@student.ethz.ch>
Subject: [PATCH 1/2] basic priority queue implementation
Date: Wed, 29 Aug 2012 07:10:56 -0400 [thread overview]
Message-ID: <20120829111056.GA14734@sigill.intra.peff.net> (raw)
In-Reply-To: <20120829110812.GA14069@sigill.intra.peff.net>
This can provide better algorithmic complexity for some of
the date-sorted commit list uses, among other things.
The queue is stored as a heap, allowing O(log) insertion and
top-element removal, and O(1) peeking at the top element.
Current commit lists are sorted linked lists, giving O(1)
peeking and top-element removal, but O(n^2) insertion.
Signed-off-by: Jeff King <peff@peff.net>
---
Mostly the same as $gmane/174007, but rebased on top of jc/merge-bases,
and adding queue_clear() to avoid leaking memory.
.gitignore | 1 +
Makefile | 3 +++
queue.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
queue.h | 18 ++++++++++++++
t/t0007-queue.sh | 50 +++++++++++++++++++++++++++++++++++++
test-queue.c | 39 +++++++++++++++++++++++++++++
6 files changed, 186 insertions(+)
create mode 100644 queue.c
create mode 100644 queue.h
create mode 100755 t/t0007-queue.sh
create mode 100644 test-queue.c
diff --git a/.gitignore b/.gitignore
index 3b7680e..f4539e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -184,6 +184,7 @@
/test-obj-pool
/test-parse-options
/test-path-utils
+/test-queue
/test-run-command
/test-sha1
/test-sigchain
diff --git a/Makefile b/Makefile
index e4f8e0e..a37d962 100644
--- a/Makefile
+++ b/Makefile
@@ -476,6 +476,7 @@ TEST_PROGRAMS_NEED_X += test-path-utils
TEST_PROGRAMS_NEED_X += test-obj-pool
TEST_PROGRAMS_NEED_X += test-parse-options
TEST_PROGRAMS_NEED_X += test-path-utils
+TEST_PROGRAMS_NEED_X += test-queue
TEST_PROGRAMS_NEED_X += test-run-command
TEST_PROGRAMS_NEED_X += test-sha1
TEST_PROGRAMS_NEED_X += test-sigchain
@@ -597,6 +598,7 @@ LIB_H += prompt.h
LIB_H += pkt-line.h
LIB_H += progress.h
LIB_H += prompt.h
+LIB_H += queue.h
LIB_H += quote.h
LIB_H += reflog-walk.h
LIB_H += refs.h
@@ -709,6 +711,7 @@ LIB_OBJS += prompt.o
LIB_OBJS += pretty.o
LIB_OBJS += progress.o
LIB_OBJS += prompt.o
+LIB_OBJS += queue.o
LIB_OBJS += quote.o
LIB_OBJS += reachable.o
LIB_OBJS += read-cache.o
diff --git a/queue.c b/queue.c
new file mode 100644
index 0000000..7be6b86
--- /dev/null
+++ b/queue.c
@@ -0,0 +1,75 @@
+#include "queue.h"
+#include "cache.h"
+
+static inline void queue_swap(struct queue *pq, unsigned i, unsigned j)
+{
+ void *tmp = pq->items[i];
+ pq->items[i] = pq->items[j];
+ pq->items[j] = tmp;
+}
+
+static void queue_heapify_up(struct queue *pq)
+{
+ unsigned i = pq->nr - 1;
+ while (i > 0) {
+ int parent = (i-1)/2;
+
+ if (pq->cmp(pq->items[i], pq->items[parent]) <= 0)
+ return;
+
+ queue_swap(pq, i, parent);
+ i = parent;
+ }
+}
+
+void queue_insert(struct queue *pq, void *item)
+{
+ ALLOC_GROW(pq->items, pq->nr + 1, pq->alloc);
+ pq->items[pq->nr++] = item;
+ queue_heapify_up(pq);
+}
+
+static void queue_heapify_down(struct queue *pq)
+{
+ unsigned i = 0;
+ while (1) {
+ int largest = i, left = 2*i + 1, right = 2*i + 2;
+
+ if (left < pq->nr &&
+ pq->cmp(pq->items[left], pq->items[largest]) > 0)
+ largest = left;
+ if (right < pq->nr &&
+ pq->cmp(pq->items[right], pq->items[largest]) > 0)
+ largest = right;
+
+ if (largest == i)
+ return;
+
+ queue_swap(pq, i, largest);
+ i = largest;
+ }
+}
+
+void *queue_peek(struct queue *pq)
+{
+ return pq->nr ? pq->items[0] : NULL;
+}
+
+void *queue_pop(struct queue *pq)
+{
+ void *ret;
+
+ if (!pq->nr)
+ return NULL;
+ ret = pq->items[0];
+
+ pq->items[0] = pq->items[--pq->nr];
+ queue_heapify_down(pq);
+
+ return ret;
+}
+
+void queue_clear(struct queue *pq)
+{
+ free(pq->items);
+}
diff --git a/queue.h b/queue.h
new file mode 100644
index 0000000..cc471b5
--- /dev/null
+++ b/queue.h
@@ -0,0 +1,18 @@
+#ifndef QUEUE_H
+#define QUEUE_H
+
+typedef int (*queue_comparison_func_t)(const void *, const void *);
+
+struct queue {
+ queue_comparison_func_t cmp;
+ void **items;
+ unsigned nr;
+ unsigned alloc;
+};
+
+void queue_insert(struct queue *pq, void *item);
+void *queue_peek(struct queue *pq);
+void *queue_pop(struct queue *pq);
+void queue_clear(struct queue *pq);
+
+#endif /* QUEUE_H */
diff --git a/t/t0007-queue.sh b/t/t0007-queue.sh
new file mode 100755
index 0000000..ee357bb
--- /dev/null
+++ b/t/t0007-queue.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='basic tests for priority queue implementation'
+. ./test-lib.sh
+
+cat >expect <<'EOF'
+10
+9
+8
+7
+6
+5
+5
+4
+3
+2
+1
+EOF
+test_expect_success 'basic ordering' '
+ test-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+3
+5
+4
+6
+2
+1
+EOF
+test_expect_success 'mixed insert and pop' '
+ test-queue 1 2 3 pop 4 5 pop pop 6 dump >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+2
+1
+NULL
+2
+1
+NULL
+EOF
+test_expect_success 'notice empty queue' '
+ test-queue 1 2 pop pop pop 1 2 pop pop pop >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/test-queue.c b/test-queue.c
new file mode 100644
index 0000000..a1e92e2
--- /dev/null
+++ b/test-queue.c
@@ -0,0 +1,39 @@
+#include "cache.h"
+#include "queue.h"
+
+static int intcmp(const void *va, const void *vb)
+{
+ const int *a = va, *b = vb;
+ return *a - *b;
+}
+
+static void show(int *v)
+{
+ if (!v)
+ printf("NULL\n");
+ else
+ printf("%d\n", *v);
+ free(v);
+}
+
+int main(int argc, char **argv)
+{
+ struct queue pq = { intcmp };
+
+ while (*++argv) {
+ if (!strcmp(*argv, "pop"))
+ show(queue_pop(&pq));
+ else if (!strcmp(*argv, "dump")) {
+ int *v;
+ while ((v = queue_pop(&pq)))
+ show(v);
+ }
+ else {
+ int *v = malloc(sizeof(*v));
+ *v = atoi(*argv);
+ queue_insert(&pq, v);
+ }
+ }
+
+ return 0;
+}
--
1.7.12.35.g38689e3
next prev parent reply other threads:[~2012-08-29 11:11 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-08-27 23:11 [PATCH 0/5] optimize fast-forward checks Junio C Hamano
2012-08-27 23:11 ` [PATCH 1/5] in_merge_bases(): support only one "other" commit Junio C Hamano
2012-08-27 23:12 ` [PATCH 2/5] receive-pack: use in_merge_bases() for fast-forward check Junio C Hamano
2012-08-27 23:12 ` [PATCH 3/5] http-push: " Junio C Hamano
2012-08-27 23:12 ` [PATCH 4/5] in_merge_bases(): omit unnecessary redundant common ancestor reduction Junio C Hamano
2012-08-27 23:12 ` [PATCH 5/5] (BROKEN) get_merge_bases_many(): walk from many tips in parallel Junio C Hamano
2012-08-28 1:25 ` Junio C Hamano
2012-08-28 21:35 ` Junio C Hamano
2012-08-28 23:39 ` Junio C Hamano
2012-08-29 11:08 ` Jeff King
2012-08-29 11:10 ` Jeff King [this message]
2012-08-29 11:11 ` [PATCH 2/2] commit: use a priority queue in merge base functions Jeff King
2012-08-29 16:36 ` Junio C Hamano
2012-08-29 20:53 ` Jeff King
2012-08-29 20:55 ` Jeff King
2012-08-29 21:00 ` Jeff King
2012-08-29 21:05 ` Jeff King
2012-08-30 12:54 ` Jeff King
2012-08-30 13:03 ` Jeff King
2012-08-30 13:24 ` Jeff King
2012-08-30 16:33 ` Junio C Hamano
2012-08-30 21:48 ` Jeff King
2012-08-30 22:16 ` Junio C Hamano
2012-08-30 16:23 ` Junio C Hamano
2012-08-30 21:31 ` Jeff King
2012-08-30 21:59 ` Junio C Hamano
2012-08-29 21:18 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20120829111056.GA14734@sigill.intra.peff.net \
--to=peff@peff.net \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=trast@student.ethz.ch \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).