If bitmap indexes are available, the process of counting reachable
commits with `git rev-list --count` can be greatly sped up. Instead of
having to use callbacks that yield each object in the revision list, we
can build the reachable bitmap for the list and then use an efficient
popcount to find the number of bits set in the bitmap.

This commit implements a `count_bitmap_commit_list` that can be used
after `prepare_bitmap_walk` has returned successfully to return the
number of commits, trees, blobs or tags that have been found to be
reachable during the walk.

`git rev-list` is taught to use this function call when bitmaps are
enabled instead of going through the old rev-list machinery. Do note,
however, that counts with `left_right` and `cherry_mark` are not
optimized by this patch.

Here are some sample timings of different ways to count commits in
`torvalds/linux`:

        $ time ../git/git rev-list master | wc -l
        376549

        real    0m6.973s
        user    0m3.216s
        sys     0m5.316s

        $ time ../git/git rev-list --count master
        376549

        real    0m1.933s
        user    0m1.744s
        sys     0m0.188s

        $ time ../git/git rev-list --use-bitmaps --count master
        376549

        real    0m0.005s
        user    0m0.000s
        sys     0m0.004s

Note that the time in the `--use-bitmaps` invocation is basically noise.
In my machine it ranges from 2ms to 6ms.
---
 builtin/rev-list.c |   11 +++++++++--
 pack-bitmap.c      |   37 +++++++++++++++++++++++++++++++++++++
 pack-bitmap.h      |    2 ++
 3 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 905ed08..097adb8 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -354,8 +354,15 @@ int cmd_rev_list(int argc, const char **argv, const char 
*prefix)
                revs.limited = 1;
 
        if (use_bitmaps && !prepare_bitmap_walk(&revs, NULL)) {
-               traverse_bitmap_commit_list(&show_object_fast);
-               return 0;
+               if (revs.count && !revs.left_right && !revs.cherry_mark) {
+                       uint32_t commit_count;
+                       count_bitmap_commit_list(&commit_count, NULL, NULL, 
NULL);
+                       printf("%d\n", commit_count);
+                       return 0;
+               } else {
+                       traverse_bitmap_commit_list(&show_object_fast);
+                       return 0;
+               }
        }
 
        if (prepare_revision_walk(&revs))
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 090db15..65fdce7 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -720,6 +720,43 @@ void traverse_bitmap_commit_list(show_reachable_fn 
show_reachable)
        bitmap_git.result = NULL;
 }
 
+static uint32_t count_object_type(
+       struct bitmap *objects,
+       struct ewah_bitmap *type_filter)
+{
+       size_t i = 0, count = 0;
+       struct ewah_iterator it;
+       eword_t filter;
+
+       ewah_iterator_init(&it, type_filter);
+
+       while (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {
+               eword_t word = objects->words[i++] & filter;
+               count += __builtin_popcountll(word);
+       }
+
+       return count;
+}
+
+void count_bitmap_commit_list(
+       uint32_t *commits, uint32_t *trees, uint32_t *blobs, uint32_t *tags)
+{
+       if (!bitmap_git.result)
+               die("Tried to count bitmap without setting it up first");
+
+       if (commits)
+               *commits = count_object_type(bitmap_git.result, 
bitmap_git.commits);
+
+       if (trees)
+               *trees = count_object_type(bitmap_git.result, bitmap_git.trees);
+
+       if (blobs)
+               *blobs = count_object_type(bitmap_git.result, bitmap_git.blobs);
+
+       if (tags)
+               *tags = count_object_type(bitmap_git.result, bitmap_git.tags);
+}
+
 struct bitmap_test_data {
        struct bitmap *base;
        struct progress *prg;
diff --git a/pack-bitmap.h b/pack-bitmap.h
index 8e7e3dc..816da6d 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -47,6 +47,8 @@ typedef int (*show_reachable_fn)(
        struct packed_git *found_pack,
        off_t found_offset);
 
+void count_bitmap_commit_list(
+       uint32_t *commits, uint32_t *trees, uint32_t *blobs, uint32_t *tags);
 void traverse_bitmap_commit_list(show_reachable_fn show_reachable);
 int prepare_bitmap_walk(struct rev_info *revs, uint32_t *result_size);
 void test_bitmap_walk(struct rev_info *revs);
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to