Skip to content

Commit 7925306

Browse files
pks-tgitster
authored andcommitted
builtin/cat-file: use bitmaps to efficiently filter by object type
While it is now possible to filter objects by type, this mechanism is for now mostly a convenience. Most importantly, we still have to iterate through the whole packfile to find all objects of a specific type. This can be prohibitively expensive depending on the size of the packfiles. It isn't really possible to do better than this when only considering a packfile itself, as the order of objects is not fixed. But when we have a packfile with a corresponding bitmap, either because the packfile itself has one or because the multi-pack index has a bitmap for it, then we can use these bitmaps to improve the runtime. While bitmaps are typically used to compute reachability of objects, they also contain one bitmap per object type encodes which object has what type. So instead of reading through the whole packfile(s), we can use the bitmaps and iterate through the type-specific bitmap. Typically, only a subset of packfiles will have a bitmap. But this isn't really much of a problem: we can use bitmaps when available, and then use the non-bitmap walk for every packfile that isn't covered by one. Overall, this leads to quite a significant speedup depending on how many objects of a certain type exist. The following benchmarks have been executed in the Chromium repository, which has a 50GB packfile with almost 25 million objects: Benchmark 1: git cat-file --batch-check --batch-all-objects --unordered --buffer --no-objects-filter Time (mean ± σ): 82.806 s ± 6.363 s [User: 30.956 s, System: 8.264 s] Range (min … max): 73.936 s … 89.690 s 10 runs Benchmark 2: git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=object:type=tag Time (mean ± σ): 20.8 ms ± 1.3 ms [User: 6.1 ms, System: 14.5 ms] Range (min … max): 18.2 ms … 23.6 ms 127 runs Benchmark 3: git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=object:type=commit Time (mean ± σ): 1.551 s ± 0.008 s [User: 1.401 s, System: 0.147 s] Range (min … max): 1.541 s … 1.566 s 10 runs Benchmark 4: git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=object:type=tree Time (mean ± σ): 11.169 s ± 0.046 s [User: 10.076 s, System: 1.063 s] Range (min … max): 11.114 s … 11.245 s 10 runs Benchmark 5: git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=object:type=blob Time (mean ± σ): 67.342 s ± 3.368 s [User: 20.318 s, System: 7.787 s] Range (min … max): 62.836 s … 73.618 s 10 runs Benchmark 6: git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=blob:none Time (mean ± σ): 13.032 s ± 0.072 s [User: 11.638 s, System: 1.368 s] Range (min … max): 12.960 s … 13.199 s 10 runs Summary git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=object:type=tag 74.75 ± 4.61 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=object:type=commit 538.17 ± 33.17 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=object:type=tree 627.98 ± 38.77 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=blob:none 3244.93 ± 257.23 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --objects-filter=object:type=blob 3990.07 ± 392.72 times faster than git cat-file --batch-check --batch-all-objects --unordered --buffer --no-objects-filter The first benchmark is mostly equivalent in runtime compared to all the others without the bitmap-optimization introduced in this commit. What is noticeable in the benchmarks is that we're I/O-bound, not CPU-bound, as can be seen from the user/system runtimes, which is often way lower than the overall benchmarked runtime. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1061745 commit 7925306

File tree

1 file changed

+50
-5
lines changed

1 file changed

+50
-5
lines changed

builtin/cat-file.c

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "streaming.h"
2222
#include "oid-array.h"
2323
#include "packfile.h"
24+
#include "pack-bitmap.h"
2425
#include "object-file.h"
2526
#include "object-name.h"
2627
#include "object-store-ll.h"
@@ -805,17 +806,61 @@ static int batch_one_object_packed(const struct object_id *oid,
805806
payload->payload);
806807
}
807808

808-
static void batch_each_object(for_each_object_fn callback,
809+
static int batch_one_object_bitmapped(const struct object_id *oid,
810+
enum object_type type UNUSED,
811+
int flags UNUSED,
812+
uint32_t hash UNUSED,
813+
struct packed_git *pack,
814+
off_t offset,
815+
void *_payload)
816+
{
817+
struct for_each_object_payload *payload = _payload;
818+
return payload->callback(oid, pack, offset, payload->payload);
819+
}
820+
821+
static void batch_each_object(struct batch_options *opt,
822+
for_each_object_fn callback,
809823
unsigned flags,
810824
void *_payload)
811825
{
812826
struct for_each_object_payload payload = {
813827
.callback = callback,
814828
.payload = _payload,
815829
};
830+
struct bitmap_index *bitmap = prepare_bitmap_git(the_repository);
831+
816832
for_each_loose_object(batch_one_object_loose, &payload, 0);
817-
for_each_packed_object(the_repository, batch_one_object_packed,
818-
&payload, flags);
833+
834+
if (bitmap &&
835+
(opt->objects_filter.choice == LOFC_OBJECT_TYPE ||
836+
opt->objects_filter.choice == LOFC_BLOB_NONE)) {
837+
struct packed_git *pack;
838+
839+
if (opt->objects_filter.choice == LOFC_OBJECT_TYPE) {
840+
for_each_bitmapped_object(bitmap, opt->objects_filter.object_type,
841+
batch_one_object_bitmapped, &payload);
842+
} else {
843+
for_each_bitmapped_object(bitmap, OBJ_COMMIT,
844+
batch_one_object_bitmapped, &payload);
845+
for_each_bitmapped_object(bitmap, OBJ_TAG,
846+
batch_one_object_bitmapped, &payload);
847+
for_each_bitmapped_object(bitmap, OBJ_TREE,
848+
batch_one_object_bitmapped, &payload);
849+
}
850+
851+
for (pack = get_all_packs(the_repository); pack; pack = pack->next) {
852+
if (bitmap_index_contains_pack(bitmap, pack) ||
853+
open_pack_index(pack))
854+
continue;
855+
for_each_object_in_pack(pack, batch_one_object_packed,
856+
&payload, flags);
857+
}
858+
} else {
859+
for_each_packed_object(the_repository, batch_one_object_packed,
860+
&payload, flags);
861+
}
862+
863+
free_bitmap_index(bitmap);
819864
}
820865

821866
static int batch_objects(struct batch_options *opt)
@@ -872,14 +917,14 @@ static int batch_objects(struct batch_options *opt)
872917

873918
cb.seen = &seen;
874919

875-
batch_each_object(batch_unordered_object,
920+
batch_each_object(opt, batch_unordered_object,
876921
FOR_EACH_OBJECT_PACK_ORDER, &cb);
877922

878923
oidset_clear(&seen);
879924
} else {
880925
struct oid_array sa = OID_ARRAY_INIT;
881926

882-
batch_each_object(collect_object, 0, &sa);
927+
batch_each_object(opt, collect_object, 0, &sa);
883928
oid_array_for_each_unique(&sa, batch_object_cb, &cb);
884929

885930
oid_array_clear(&sa);

0 commit comments

Comments
 (0)