]> git.ipfire.org Git - thirdparty/git.git/commitdiff
describe: use oidset in finish_depth_computation()
authorRené Scharfe <l.s.r@web.de>
Tue, 2 Sep 2025 18:24:52 +0000 (20:24 +0200)
committerJunio C Hamano <gitster@pobox.com>
Tue, 2 Sep 2025 22:15:13 +0000 (15:15 -0700)
Depth computation can end early if all remaining commits are flagged.
The current code determines if that's the case by checking all queue
items each time it dequeues a flagged commit.  This can cause
quadratic complexity.

We could simply count the flagged items in the queue and then update
that number as we add and remove items.  That would provide a general
speedup, but leave one case where we have to scan the whole queue: When
we flag a previously seen, but unflagged commit.  It could be on the
queue and then we'd have to decrease our count.

We could dedicate an object flag to track queue membership, but that
would leave less for candidate tags, affecting the results.  So use a
hash table, specifically an oidset of commit hashes, to track that.
This avoids quadratic behaviour in all cases and provides a nice
performance boost over the previous commit, 08bb69d70f (describe: use
prio_queue_replace(), 2025-08-03):

Benchmark 1: ./git_08bb69d70f describe $(git rev-list v2.41.0..v2.47.0)
  Time (mean ± σ):     855.3 ms ±   1.3 ms    [User: 790.8 ms, System: 49.9 ms]
  Range (min … max):   853.7 ms … 857.8 ms    10 runs

Benchmark 2: ./git describe $(git rev-list v2.41.0..v2.47.0)
  Time (mean ± σ):     610.8 ms ±   1.7 ms    [User: 546.9 ms, System: 49.3 ms]
  Range (min … max):   608.9 ms … 613.3 ms    10 runs

Summary
  ./git describe $(git rev-list v2.41.0..v2.47.0) ran
    1.40 ± 0.00 times faster than ./git_08bb69d70f describe $(git rev-list v2.41.0..v2.47.0)

Helped-by: Jeff King <peff@peff.net>
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/describe.c

index c18e4b3e4b714f1bac9e0f991e9473de81127304..deb84a84297bc3ec04de737e560ae7269e45063f 100644 (file)
@@ -24,6 +24,7 @@
 #include "commit-slab.h"
 #include "wildmatch.h"
 #include "prio-queue.h"
+#include "oidset.h"
 
 #define MAX_TAGS       (FLAG_BITS - 1)
 #define DEFAULT_CANDIDATES 10
@@ -286,38 +287,47 @@ static void lazy_queue_clear(struct lazy_queue *queue)
        queue->get_pending = false;
 }
 
-static bool all_have_flag(const struct lazy_queue *queue, unsigned flag)
+static unsigned long finish_depth_computation(struct lazy_queue *queue,
+                                             struct possible_tag *best)
 {
+       unsigned long seen_commits = 0;
+       struct oidset unflagged = OIDSET_INIT;
+
        for (size_t i = queue->get_pending ? 1 : 0; i < queue->queue.nr; i++) {
                struct commit *commit = queue->queue.array[i].data;
-               if (!(commit->object.flags & flag))
-                       return false;
+               if (!(commit->object.flags & best->flag_within))
+                       oidset_insert(&unflagged, &commit->object.oid);
        }
-       return true;
-}
 
-static unsigned long finish_depth_computation(struct lazy_queue *queue,
-                                             struct possible_tag *best)
-{
-       unsigned long seen_commits = 0;
        while (!lazy_queue_empty(queue)) {
                struct commit *c = lazy_queue_get(queue);
                struct commit_list *parents = c->parents;
                seen_commits++;
                if (c->object.flags & best->flag_within) {
-                       if (all_have_flag(queue, best->flag_within))
+                       if (!oidset_size(&unflagged))
                                break;
-               } else
+               } else {
+                       oidset_remove(&unflagged, &c->object.oid);
                        best->depth++;
+               }
                while (parents) {
+                       unsigned seen, flag_before, flag_after;
                        struct commit *p = parents->item;
                        repo_parse_commit(the_repository, p);
-                       if (!(p->object.flags & SEEN))
+                       seen = p->object.flags & SEEN;
+                       if (!seen)
                                lazy_queue_put(queue, p);
+                       flag_before = p->object.flags & best->flag_within;
                        p->object.flags |= c->object.flags;
+                       flag_after = p->object.flags & best->flag_within;
+                       if (!seen && !flag_after)
+                               oidset_insert(&unflagged, &p->object.oid);
+                       if (seen && !flag_before && flag_after)
+                               oidset_remove(&unflagged, &p->object.oid);
                        parents = parents->next;
                }
        }
+       oidset_clear(&unflagged);
        return seen_commits;
 }