pack-bitmap: pass object position to `fill_bitmap_tree()`

In the following commit, callers of `fill_bitmap_tree()` will be required to check the bit corresponding to their tree before calling that function. That change will reduce the overhead of setting up and tearing down stack frames for trees whose bits are already set. To prepare for that change, have callers pass in the tree's bit position in `fill_bitmap_tree()`, which will make the next commit easier to read. In the meantime, this change has a surprising and measurable benefit during bitmap generation, particularly on very large repositories. When processing sub-trees within `fill_bitmap_tree()`, the preimage of this patch did the following: while (tree_entry(&desc, entry)) { switch (object_type(entry.mode)) { case OBJ_TREE: if (fill_bitmap_tree(writer, bitmap, lookup_tree(writer->repo, &entry.oid)) < 0) { /* ... */ } /* ... */ } } , first performing the object lookup via `lookup_tree()`, and then locating its bit position within the recursive call. This patch effectively reorders those two calls so that we first discover the sub-tree's bit position, *then* load its tree. By reordering these two operations, we spend fewer CPU cycles per instruction, likely due to improved CPU dependency/cache/pipeline behavior. Comparing the results of: running `perf stat` before and after this commit, we have: +--------------+-------------+-------------+-------------------+ | | HEAD^ | HEAD | Delta | +--------------+-------------+-------------+-------------------+ | elapsed | 612.5 s | 582.4 s | -30.1 s (-4.9%) | | cycles | 2,857.3 B | 2,713.3 B | -144.0 B (-5.0%) | | instructions | 2,413.2 B | 2,415.5 B | +2.3 B (+0.1%) | | CPI | 1.184 | 1.123 | -0.061 (-5.1%) | +--------------+-------------+-------------+-------------------+ In a large repository with ~4.8M commit, and ~37.1M tree objects this change improves timing from ~612.5 seconds down to ~582.4 seconds, or a ~4.9% improvement. More importantly, the number of CPU cycles spent dropped off significantly as a result of this commit, lowering our cycles-per-instruction ratio by about ~5.1%. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Taylor Blau committed May 27, 2026 at 15:55 UTC e3959cc78c968d8f029daa48d4aadcb486da0629
1 file changed +15 -8
pack-bitmap-write.c
+15 -8
@@ -456,10 +456,10 @@ static void bitmap_builder_clear(struct bitmap_builder *bb)
456
457 static int fill_bitmap_tree(struct bitmap_writer *writer,
458 struct bitmap *bitmap,
459 - struct tree *tree)
459 + struct tree *tree,
460 + uint32_t pos)
461 {
462 int found;
462 - uint32_t pos;
463 struct tree_desc desc;
464 struct name_entry entry;
465
@@ -467,9 +467,6 @@ static int fill_bitmap_tree(struct bitmap_writer *writer,
467 * If our bit is already set, then there is nothing to do. Both this
468 * tree and all of its children will be set.
469 */
470 - pos = find_object_pos(writer, &tree->object.oid, &found);
471 - if (!found)
472 - return -1;
470 if (bitmap_get(bitmap, pos))
471 return 0;
472 bitmap_set(bitmap, pos);
@@ -482,8 +479,12 @@ static int fill_bitmap_tree(struct bitmap_writer *writer,
479 while (tree_entry(&desc, &entry)) {
480 switch (object_type(entry.mode)) {
481 case OBJ_TREE:
482 + pos = find_object_pos(writer, &entry.oid, &found);
483 + if (!found)
484 + return -1;
485 if (fill_bitmap_tree(writer, bitmap,
486 - lookup_tree(writer->repo, &entry.oid)) < 0)
486 + lookup_tree(writer->repo,
487 + &entry.oid), pos) < 0)
488 return -1;
489 break;
490 case OBJ_BLOB:
@@ -575,8 +576,14 @@ static int fill_bitmap_commit(struct bitmap_writer *writer,
576 }
577
578 while (tree_queue->nr) {
578 - if (fill_bitmap_tree(writer, ent->bitmap,
579 - prio_queue_get(tree_queue)) < 0)
579 + struct tree *t = prio_queue_get(tree_queue);
580 + int found;
581 +
582 + pos = find_object_pos(writer, &t->object.oid, &found);
583 + if (!found)
584 + return -1;
585 +
586 + if (fill_bitmap_tree(writer, ent->bitmap, t, pos) < 0)
587 return -1;
588 }
589 return 0;