sha1_name: minimize OID comparisons during disambiguation

Minimize OID comparisons during disambiguation of packfile OIDs. Teach git to use binary search with the full OID to find the object's position (or insertion position, if not present) in the pack-index. The object before and immediately after (or the one at the insertion position) give the maximum common prefix. No subsequent linear search is required. Take care of which two to inspect, in case the object id exists in the packfile. If the input to find_unique_abbrev_r() is a partial prefix, then the OID used for the binary search is padded with zeroes so the object will not exist in the repo (with high probability) and the same logic applies. This commit completes a series of three changes to OID abbreviation code, and the overall change can be seen using standard commands for large repos. Below we report performance statistics for perf test 4211.6 from p4211-line-log.sh using three copies of the Linux repo: | Packs | Loose | HEAD~3 | HEAD | Rel% | |-------|--------|----------|----------|-------| | 1 | 0 | 41.27 s | 38.93 s | -4.8% | | 24 | 0 | 98.04 s | 91.35 s | -5.7% | | 23 | 323952 | 117.78 s | 112.18 s | -4.8% | Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Reviewed-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Oct 12, 2017 at 08:02 UTC 0e87b85683df145007862d23b5f0773368d66464
1 file changed +71 -5
sha1_name.c
+71 -5
@@ -153,7 +153,9 @@ static void unique_in_pack(struct packed_git *p,
153 uint32_t num, last, i, first = 0;
154 const struct object_id *current = NULL;
155
156 - open_pack_index(p);
156 + if (open_pack_index(p) || !p->num_objects)
157 + return;
158 +
159 num = p->num_objects;
160 last = num;
161 while (first < last) {
@@ -478,6 +480,7 @@ struct min_abbrev_data {
480 unsigned int init_len;
481 unsigned int cur_len;
482 char *hex;
483 + const unsigned char *hash;
484 };
485
486 static inline char get_hex_char_from_oid(const struct object_id *oid,
@@ -505,6 +508,67 @@ static int extend_abbrev_len(const struct object_id *oid, void *cb_data)
508 return 0;
509 }
510
511 +static void find_abbrev_len_for_pack(struct packed_git *p,
512 + struct min_abbrev_data *mad)
513 +{
514 + int match = 0;
515 + uint32_t num, last, first = 0;
516 + struct object_id oid;
517 +
518 + if (open_pack_index(p) || !p->num_objects)
519 + return;
520 +
521 + num = p->num_objects;
522 + last = num;
523 + while (first < last) {
524 + uint32_t mid = first + (last - first) / 2;
525 + const unsigned char *current;
526 + int cmp;
527 +
528 + current = nth_packed_object_sha1(p, mid);
529 + cmp = hashcmp(mad->hash, current);
530 + if (!cmp) {
531 + match = 1;
532 + first = mid;
533 + break;
534 + }
535 + if (cmp > 0) {
536 + first = mid + 1;
537 + continue;
538 + }
539 + last = mid;
540 + }
541 +
542 + /*
543 + * first is now the position in the packfile where we would insert
544 + * mad->hash if it does not exist (or the position of mad->hash if
545 + * it does exist). Hence, we consider a maximum of three objects
546 + * nearby for the abbreviation length.
547 + */
548 + mad->init_len = 0;
549 + if (!match) {
550 + nth_packed_object_oid(&oid, p, first);
551 + extend_abbrev_len(&oid, mad);
552 + } else if (first < num - 1) {
553 + nth_packed_object_oid(&oid, p, first + 1);
554 + extend_abbrev_len(&oid, mad);
555 + }
556 + if (first > 0) {
557 + nth_packed_object_oid(&oid, p, first - 1);
558 + extend_abbrev_len(&oid, mad);
559 + }
560 + mad->init_len = mad->cur_len;
561 +}
562 +
563 +static void find_abbrev_len_packed(struct min_abbrev_data *mad)
564 +{
565 + struct packed_git *p;
566 +
567 + prepare_packed_git();
568 + for (p = packed_git; p; p = p->next)
569 + find_abbrev_len_for_pack(p, mad);
570 +}
571 +
572 int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
573 {
574 struct disambiguate_state ds;
@@ -536,19 +600,21 @@ int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
600 if (len == GIT_SHA1_HEXSZ || !len)
601 return GIT_SHA1_HEXSZ;
602
539 - if (init_object_disambiguation(hex, len, &ds) < 0)
540 - return -1;
541 -
603 mad.init_len = len;
604 mad.cur_len = len;
605 mad.hex = hex;
606 + mad.hash = sha1;
607 +
608 + find_abbrev_len_packed(&mad);
609 +
610 + if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0)
611 + return -1;
612
613 ds.fn = extend_abbrev_len;
614 ds.always_call_fn = 1;
615 ds.cb_data = (void *)&mad;
616
617 find_short_object_filename(&ds);
551 - find_short_packed_object(&ds);
618 (void)finish_object_disambiguation(&ds, &oid_ret);
619
620 hex[mad.cur_len] = 0;