3131#include "run-command.h"
3232#include "setup.h"
3333#include "strvec.h"
34+ #include "trace2.h"
3435
3536static const char index_pack_usage [] =
3637"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict[=<msg-id>=<severity>...]] [--fsck-objects[=<msg-id>=<severity>...]] (<pack-file> | --stdin [--fix-thin] [<pack-file>])" ;
@@ -43,6 +44,43 @@ struct object_entry {
4344 signed char real_type ;
4445};
4546
47+ /*
48+ * Hash workers own no repository state. They only hash immutable full-blob
49+ * buffers; the main thread retires their results and performs the usual
50+ * collision/content checks before releasing those buffers.
51+ */
52+ struct first_pass_hash_job {
53+ struct object_entry * obj ;
54+ struct object_id oid ;
55+ void * data ;
56+ int done ;
57+ };
58+
59+ struct first_pass_hash_pool {
60+ pthread_t * threads ;
61+ pthread_mutex_t mutex ;
62+ pthread_cond_t work_ready ;
63+ pthread_cond_t result_ready ;
64+ struct first_pass_hash_job * queue ;
65+ size_t nr_threads , queue_size ;
66+ size_t first , next_work , nr , pending ;
67+ size_t buffered , jobs ;
68+ int stop ;
69+ };
70+
71+ #define FIRST_PASS_HASH_MAX_THREADS 32
72+
73+ static int first_pass_hash_threads ;
74+ static size_t first_pass_hash_buffer_size = 64 * 1024 * 1024 ;
75+ static size_t first_pass_hash_min_size = 64 * 1024 ;
76+ static struct first_pass_hash_pool first_pass_hash_pool ;
77+
78+ static struct first_pass_hash_job * reserve_first_pass_hash (struct object_entry * obj );
79+ static void submit_first_pass_hash (struct first_pass_hash_job * job ,
80+ struct object_entry * obj , void * data );
81+ static void start_first_pass_hash (void );
82+ static void finish_first_pass_hash (void );
83+
4684struct object_stat {
4785 unsigned delta_depth ;
4886 int base_object_no ;
@@ -479,7 +517,7 @@ static void *unpack_entry_data(off_t offset, size_t size,
479517 char hdr [32 ];
480518 int hdrlen ;
481519
482- if (!is_delta_type (type )) {
520+ if (!is_delta_type (type ) && oid ) {
483521 hdrlen = format_object_header (hdr , sizeof (hdr ), type , size );
484522 git_hash_init (& c , the_hash_algo );
485523 git_hash_update (& c , hdr , hdrlen );
@@ -520,7 +558,8 @@ static void *unpack_entry_data(off_t offset, size_t size,
520558static void * unpack_raw_entry (struct object_entry * obj ,
521559 off_t * ofs_offset ,
522560 struct object_id * ref_oid ,
523- struct object_id * oid )
561+ struct object_id * oid ,
562+ struct first_pass_hash_job * * hash_job )
524563{
525564 unsigned char * p ;
526565 size_t size , c ;
@@ -582,7 +621,9 @@ static void *unpack_raw_entry(struct object_entry *obj,
582621 }
583622 obj -> hdr_size = consumed_bytes - obj -> idx .offset ;
584623
585- data = unpack_entry_data (obj -> idx .offset , obj -> size , obj -> type , oid );
624+ * hash_job = reserve_first_pass_hash (obj );
625+ data = unpack_entry_data (obj -> idx .offset , obj -> size , obj -> type ,
626+ * hash_job ? NULL : oid );
586627 obj -> idx .crc32 = input_crc32 ;
587628 return data ;
588629}
@@ -978,6 +1019,171 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
9781019 free (new_data );
9791020}
9801021
1022+ static void * first_pass_hash_worker (void * data UNUSED )
1023+ {
1024+ struct first_pass_hash_pool * pool = & first_pass_hash_pool ;
1025+
1026+ trace2_thread_start ("index-pack-hash" );
1027+ for (;;) {
1028+ struct first_pass_hash_job * job ;
1029+
1030+ pthread_mutex_lock (& pool -> mutex );
1031+ while (!pool -> pending && !pool -> stop )
1032+ pthread_cond_wait (& pool -> work_ready , & pool -> mutex );
1033+ if (!pool -> pending ) {
1034+ pthread_mutex_unlock (& pool -> mutex );
1035+ break ;
1036+ }
1037+ job = & pool -> queue [pool -> next_work ];
1038+ pool -> next_work = (pool -> next_work + 1 ) % pool -> queue_size ;
1039+ pool -> pending -- ;
1040+ pthread_mutex_unlock (& pool -> mutex );
1041+
1042+ /* This uses the same collision-detecting hash as the serial path. */
1043+ hash_object_file (the_hash_algo , job -> data , job -> obj -> size ,
1044+ OBJ_BLOB , & job -> oid );
1045+
1046+ pthread_mutex_lock (& pool -> mutex );
1047+ job -> done = 1 ;
1048+ pthread_cond_signal (& pool -> result_ready );
1049+ pthread_mutex_unlock (& pool -> mutex );
1050+ }
1051+ trace2_thread_exit ();
1052+ return NULL ;
1053+ }
1054+
1055+ static void retire_first_pass_hash (void )
1056+ {
1057+ struct first_pass_hash_pool * pool = & first_pass_hash_pool ;
1058+ struct first_pass_hash_job * job ;
1059+ size_t allocation ;
1060+
1061+ pthread_mutex_lock (& pool -> mutex );
1062+ if (!pool -> nr )
1063+ BUG ("no queued first-pass hash to retire" );
1064+ job = & pool -> queue [pool -> first ];
1065+ while (!job -> done )
1066+ pthread_cond_wait (& pool -> result_ready , & pool -> mutex );
1067+ pool -> first = (pool -> first + 1 ) % pool -> queue_size ;
1068+ pool -> nr -- ;
1069+ pthread_mutex_unlock (& pool -> mutex );
1070+
1071+ /* All ODB and object-cache access stays on the main thread. */
1072+ oidcpy (& job -> obj -> idx .oid , & job -> oid );
1073+ sha1_object (job -> data , NULL , job -> obj -> size , OBJ_BLOB ,
1074+ & job -> obj -> idx .oid );
1075+ allocation = st_add (job -> obj -> size , 1 );
1076+ free (job -> data );
1077+ pool -> buffered -= allocation ;
1078+ }
1079+
1080+ static struct first_pass_hash_job * reserve_first_pass_hash (struct object_entry * obj )
1081+ {
1082+ struct first_pass_hash_pool * pool = & first_pass_hash_pool ;
1083+ size_t allocation ;
1084+
1085+ if (!pool -> nr_threads || obj -> type != OBJ_BLOB ||
1086+ obj -> size < first_pass_hash_min_size ||
1087+ obj -> size >= first_pass_hash_buffer_size ||
1088+ obj -> size > repo_settings_get_big_file_threshold (the_repository ))
1089+ return NULL ;
1090+
1091+ allocation = st_add (obj -> size , 1 );
1092+ while (pool -> nr == pool -> queue_size ||
1093+ allocation > first_pass_hash_buffer_size - pool -> buffered )
1094+ retire_first_pass_hash ();
1095+
1096+ /* Reserve before the producer allocates the inflated blob. */
1097+ pool -> buffered += allocation ;
1098+ return & pool -> queue [(pool -> first + pool -> nr ) % pool -> queue_size ];
1099+ }
1100+
1101+ static void submit_first_pass_hash (struct first_pass_hash_job * job ,
1102+ struct object_entry * obj , void * data )
1103+ {
1104+ struct first_pass_hash_pool * pool = & first_pass_hash_pool ;
1105+
1106+ assert (pool -> nr_threads && data && obj -> type == OBJ_BLOB );
1107+ job -> obj = obj ;
1108+ job -> data = data ;
1109+ job -> done = 0 ;
1110+
1111+ pthread_mutex_lock (& pool -> mutex );
1112+ pool -> nr ++ ;
1113+ pool -> pending ++ ;
1114+ pool -> jobs ++ ;
1115+ pthread_cond_signal (& pool -> work_ready );
1116+ pthread_mutex_unlock (& pool -> mutex );
1117+ }
1118+
1119+ static void stop_first_pass_hash (size_t nr_threads )
1120+ {
1121+ struct first_pass_hash_pool * pool = & first_pass_hash_pool ;
1122+ size_t i ;
1123+
1124+ pthread_mutex_lock (& pool -> mutex );
1125+ pool -> stop = 1 ;
1126+ pthread_cond_broadcast (& pool -> work_ready );
1127+ pthread_mutex_unlock (& pool -> mutex );
1128+ for (i = 0 ; i < nr_threads ; i ++ )
1129+ pthread_join (pool -> threads [i ], NULL );
1130+ pthread_cond_destroy (& pool -> result_ready );
1131+ pthread_cond_destroy (& pool -> work_ready );
1132+ pthread_mutex_destroy (& pool -> mutex );
1133+ free (pool -> queue );
1134+ free (pool -> threads );
1135+ }
1136+
1137+ static void start_first_pass_hash (void )
1138+ {
1139+ struct first_pass_hash_pool * pool = & first_pass_hash_pool ;
1140+ size_t i ;
1141+ int ret ;
1142+
1143+ /* These modes share fsck/object-cache state; retain their serial path. */
1144+ if (!HAVE_THREADS || !first_pass_hash_threads || strict ||
1145+ do_fsck_object || record_outgoing_links ||
1146+ first_pass_hash_min_size >= first_pass_hash_buffer_size ) {
1147+ trace2_data_intmax ("index-pack" , the_repository ,
1148+ "first_pass_hash/threads" , 0 );
1149+ return ;
1150+ }
1151+
1152+ pool -> nr_threads = first_pass_hash_threads ;
1153+ pool -> queue_size = st_mult (pool -> nr_threads , 2 );
1154+ CALLOC_ARRAY (pool -> threads , pool -> nr_threads );
1155+ CALLOC_ARRAY (pool -> queue , pool -> queue_size );
1156+ pthread_mutex_init (& pool -> mutex , NULL );
1157+ pthread_cond_init (& pool -> work_ready , NULL );
1158+ pthread_cond_init (& pool -> result_ready , NULL );
1159+ for (i = 0 ; i < pool -> nr_threads ; i ++ ) {
1160+ ret = pthread_create (& pool -> threads [i ], NULL ,
1161+ first_pass_hash_worker , NULL );
1162+ if (ret ) {
1163+ stop_first_pass_hash (i );
1164+ die (_ ("unable to create index-pack hash thread: %s" ),
1165+ strerror (ret ));
1166+ }
1167+ }
1168+ trace2_data_intmax ("index-pack" , the_repository ,
1169+ "first_pass_hash/threads" , pool -> nr_threads );
1170+ }
1171+
1172+ static void finish_first_pass_hash (void )
1173+ {
1174+ struct first_pass_hash_pool * pool = & first_pass_hash_pool ;
1175+
1176+ if (!pool -> nr_threads )
1177+ return ;
1178+ while (pool -> nr )
1179+ retire_first_pass_hash ();
1180+
1181+ stop_first_pass_hash (pool -> nr_threads );
1182+ trace2_data_intmax ("index-pack" , the_repository ,
1183+ "first_pass_hash/jobs" , pool -> jobs );
1184+ pool -> nr_threads = 0 ;
1185+ }
1186+
9811187/*
9821188 * Ensure that this node has been reconstructed and return its contents.
9831189 *
@@ -1255,6 +1461,8 @@ static void parse_pack_objects(unsigned char *hash)
12551461 struct stat st ;
12561462 struct git_hash_ctx tmp_ctx ;
12571463
1464+ start_first_pass_hash ();
1465+
12581466 if (verbose )
12591467 progress = start_progress (
12601468 the_repository ,
@@ -1263,9 +1471,10 @@ static void parse_pack_objects(unsigned char *hash)
12631471 nr_objects );
12641472 for (i = 0 ; i < nr_objects ; i ++ ) {
12651473 struct object_entry * obj = & objects [i ];
1474+ struct first_pass_hash_job * hash_job ;
12661475 void * data = unpack_raw_entry (obj , & ofs_delta -> offset ,
12671476 & ref_delta_oid ,
1268- & obj -> idx .oid );
1477+ & obj -> idx .oid , & hash_job );
12691478 obj -> real_type = obj -> type ;
12701479 if (obj -> type == OBJ_OFS_DELTA ) {
12711480 nr_ofs_deltas ++ ;
@@ -1276,6 +1485,9 @@ static void parse_pack_objects(unsigned char *hash)
12761485 oidcpy (& ref_deltas [nr_ref_deltas ].oid , & ref_delta_oid );
12771486 ref_deltas [nr_ref_deltas ].obj_no = i ;
12781487 nr_ref_deltas ++ ;
1488+ } else if (hash_job ) {
1489+ submit_first_pass_hash (hash_job , obj , data );
1490+ data = NULL ;
12791491 } else if (!data ) {
12801492 /* large blobs, check later */
12811493 obj -> real_type = OBJ_BAD ;
@@ -1287,6 +1499,7 @@ static void parse_pack_objects(unsigned char *hash)
12871499 display_progress (progress , i + 1 );
12881500 }
12891501 objects [i ].idx .offset = consumed_bytes ;
1502+ finish_first_pass_hash ();
12901503 stop_progress (& progress );
12911504
12921505 /* Check pack integrity */
@@ -1667,6 +1880,23 @@ static int git_index_pack_config(const char *k, const char *v,
16671880{
16681881 struct pack_idx_option * opts = cb ;
16691882
1883+ if (!strcmp (k , "indexpack.hashthreads" )) {
1884+ first_pass_hash_threads = git_config_int (k , v , ctx -> kvi );
1885+ if (first_pass_hash_threads < 0 ||
1886+ first_pass_hash_threads > FIRST_PASS_HASH_MAX_THREADS )
1887+ die (_ ("%s must be between 0 and %d" ),
1888+ k , FIRST_PASS_HASH_MAX_THREADS );
1889+ return 0 ;
1890+ }
1891+ if (!strcmp (k , "indexpack.hashbuffersize" )) {
1892+ first_pass_hash_buffer_size = git_config_ulong (k , v , ctx -> kvi );
1893+ return 0 ;
1894+ }
1895+ if (!strcmp (k , "indexpack.hashminsize" )) {
1896+ first_pass_hash_min_size = git_config_ulong (k , v , ctx -> kvi );
1897+ return 0 ;
1898+ }
1899+
16701900 if (!strcmp (k , "pack.indexversion" )) {
16711901 opts -> version = git_config_int (k , v , ctx -> kvi );
16721902 if (opts -> version > 2 )
0 commit comments