pack-objects: use bitmaps when packing objects

In this patch, we use the bitmap API to perform the `Counting Objects` phase in pack-objects, rather than a traditional walk through the object graph. For a reasonably-packed large repo, the time to fetch and clone is often dominated by the full-object revision walk during the Counting Objects phase. Using bitmaps can reduce the CPU time required on the server (and therefore start sending the actual pack data with less delay). For bitmaps to be used, the following must be true: 1. We must be packing to stdout (as a normal `pack-objects` from `upload-pack` would do). 2. There must be a .bitmap index containing at least one of the "have" objects that the client is asking for. 3. Bitmaps must be enabled (they are enabled by default, but can be disabled by setting `pack.usebitmaps` to false, or by using `--no-use-bitmap-index` on the command-line). If any of these is not true, we fall back to doing a normal walk of the object graph. Here are some sample timings from a full pack of `torvalds/linux` (i.e. something very similar to what would be generated for a clone of the repository) that show the speedup produced by various methods: [existing graph traversal] $ time git pack-objects --all --stdout --no-use-bitmap-index \ </dev/null >/dev/null Counting objects: 3237103, done. Compressing objects: 100% (508752/508752), done. Total 3237103 (delta 2699584), reused 3237103 (delta 2699584) real 0m44.111s user 0m42.396s sys 0m3.544s [bitmaps only, without partial pack reuse; note that pack reuse is automatic, so timing this required a patch to disable it] $ time git pack-objects --all --stdout </dev/null >/dev/null Counting objects: 3237103, done. Compressing objects: 100% (508752/508752), done. Total 3237103 (delta 2699584), reused 3237103 (delta 2699584) real 0m5.413s user 0m5.604s sys 0m1.804s [bitmaps with pack reuse (what you get with this patch)] $ time git pack-objects --all --stdout </dev/null >/dev/null Reusing existing pack: 3237103, done. Total 3237103 (delta 0), reused 0 (delta 0) real 0m1.636s user 0m1.460s sys 0m0.172s Signed-off-by: Vicent Marti <tanoku@gmail.com> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Vicent Marti committed Dec 21, 2013 at 09:00 UTC 6b8fda2db1d69606954711b606c546c0e4e51680
2 files changed +113
Documentation/config.txt
+6
@@ -1858,6 +1858,12 @@ pack.packSizeLimit::
1858 Common unit suffixes of 'k', 'm', or 'g' are
1859 supported.
1860
1861 +pack.useBitmaps::
1862 + When true, git will use pack bitmaps (if available) when packing
1863 + to stdout (e.g., during the server side of a fetch). Defaults to
1864 + true. You should not generally need to turn this off unless
1865 + you are debugging pack bitmaps.
1866 +
1867 pager.<cmd>::
1868 If the value is boolean, turns on or off pagination of the
1869 output of a particular Git subcommand when writing to a tty.
builtin/pack-objects.c
+107
@@ -19,6 +19,7 @@
19 #include "refs.h"
20 #include "streaming.h"
21 #include "thread-utils.h"
22 +#include "pack-bitmap.h"
23
24 static const char *pack_usage[] = {
25 N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
@@ -57,6 +58,12 @@ static struct progress *progress_state;
58 static int pack_compression_level = Z_DEFAULT_COMPRESSION;
59 static int pack_compression_seen;
60
61 +static struct packed_git *reuse_packfile;
62 +static uint32_t reuse_packfile_objects;
63 +static off_t reuse_packfile_offset;
64 +
65 +static int use_bitmap_index = 1;
66 +
67 static unsigned long delta_cache_size = 0;
68 static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
69 static unsigned long cache_max_small_delta_size = 1000;
@@ -678,6 +685,46 @@ static struct object_entry **compute_write_order(void)
685 return wo;
686 }
687
688 +static off_t write_reused_pack(struct sha1file *f)
689 +{
690 + unsigned char buffer[8192];
691 + off_t to_write;
692 + int fd;
693 +
694 + if (!is_pack_valid(reuse_packfile))
695 + die("packfile is invalid: %s", reuse_packfile->pack_name);
696 +
697 + fd = git_open_noatime(reuse_packfile->pack_name);
698 + if (fd < 0)
699 + die_errno("unable to open packfile for reuse: %s",
700 + reuse_packfile->pack_name);
701 +
702 + if (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1)
703 + die_errno("unable to seek in reused packfile");
704 +
705 + if (reuse_packfile_offset < 0)
706 + reuse_packfile_offset = reuse_packfile->pack_size - 20;
707 +
708 + to_write = reuse_packfile_offset - sizeof(struct pack_header);
709 +
710 + while (to_write) {
711 + int read_pack = xread(fd, buffer, sizeof(buffer));
712 +
713 + if (read_pack <= 0)
714 + die_errno("unable to read from reused packfile");
715 +
716 + if (read_pack > to_write)
717 + read_pack = to_write;
718 +
719 + sha1write(f, buffer, read_pack);
720 + to_write -= read_pack;
721 + }
722 +
723 + close(fd);
724 + written += reuse_packfile_objects;
725 + return reuse_packfile_offset - sizeof(struct pack_header);
726 +}
727 +
728 static void write_pack_file(void)
729 {
730 uint32_t i = 0, j;
@@ -704,6 +751,15 @@ static void write_pack_file(void)
751 offset = write_pack_header(f, nr_remaining);
752 if (!offset)
753 die_errno("unable to write pack header");
754 +
755 + if (reuse_packfile) {
756 + off_t packfile_size;
757 + assert(pack_to_stdout);
758 +
759 + packfile_size = write_reused_pack(f);
760 + offset += packfile_size;
761 + }
762 +
763 nr_written = 0;
764 for (; i < to_pack.nr_objects; i++) {
765 struct object_entry *e = write_order[i];
@@ -923,6 +979,22 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
979 return 1;
980 }
981
982 +static int add_object_entry_from_bitmap(const unsigned char *sha1,
983 + enum object_type type,
984 + int flags, uint32_t name_hash,
985 + struct packed_git *pack, off_t offset)
986 +{
987 + uint32_t index_pos;
988 +
989 + if (have_duplicate_entry(sha1, 0, &index_pos))
990 + return 0;
991 +
992 + create_object_entry(sha1, type, name_hash, 0, 0, index_pos, pack, offset);
993 +
994 + display_progress(progress_state, to_pack.nr_objects);
995 + return 1;
996 +}
997 +
998 struct pbase_tree_cache {
999 unsigned char sha1[20];
1000 int ref;
@@ -2085,6 +2157,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)
2157 cache_max_small_delta_size = git_config_int(k, v);
2158 return 0;
2159 }
2160 + if (!strcmp(k, "pack.usebitmaps")) {
2161 + use_bitmap_index = git_config_bool(k, v);
2162 + return 0;
2163 + }
2164 if (!strcmp(k, "pack.threads")) {
2165 delta_search_threads = git_config_int(k, v);
2166 if (delta_search_threads < 0)
@@ -2293,6 +2369,29 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
2369 }
2370 }
2371
2372 +static int get_object_list_from_bitmap(struct rev_info *revs)
2373 +{
2374 + if (prepare_bitmap_walk(revs) < 0)
2375 + return -1;
2376 +
2377 + if (!reuse_partial_packfile_from_bitmap(
2378 + &reuse_packfile,
2379 + &reuse_packfile_objects,
2380 + &reuse_packfile_offset)) {
2381 + assert(reuse_packfile_objects);
2382 + nr_result += reuse_packfile_objects;
2383 +
2384 + if (progress) {
2385 + fprintf(stderr, "Reusing existing pack: %d, done.\n",
2386 + reuse_packfile_objects);
2387 + fflush(stderr);
2388 + }
2389 + }
2390 +
2391 + traverse_bitmap_commit_list(&add_object_entry_from_bitmap);
2392 + return 0;
2393 +}
2394 +
2395 static void get_object_list(int ac, const char **av)
2396 {
2397 struct rev_info revs;
@@ -2320,6 +2419,9 @@ static void get_object_list(int ac, const char **av)
2419 die("bad revision '%s'", line);
2420 }
2421
2422 + if (use_bitmap_index && !get_object_list_from_bitmap(&revs))
2423 + return;
2424 +
2425 if (prepare_revision_walk(&revs))
2426 die("revision walk setup failed");
2427 mark_edges_uninteresting(&revs, show_edge);
@@ -2449,6 +2551,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
2551 N_("pack compression level")),
2552 OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
2553 N_("do not hide commits by grafts"), 0),
2554 + OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index,
2555 + N_("use a bitmap index if available to speed up counting objects")),
2556 OPT_END(),
2557 };
2558
@@ -2515,6 +2619,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
2619 if (keep_unreachable && unpack_unreachable)
2620 die("--keep-unreachable and --unpack-unreachable are incompatible.");
2621
2622 + if (!use_internal_rev_list || !pack_to_stdout || is_repository_shallow())
2623 + use_bitmap_index = 0;
2624 +
2625 if (progress && all_progress_implied)
2626 progress = 2;
2627