4545#define CACHE_EXT_UNTRACKED 0x554E5452 /* "UNTR" */
4646#define CACHE_EXT_FSMONITOR 0x46534D4E /* "FSMN" */
4747#define CACHE_EXT_ENDOFINDEXENTRIES 0x454F4945 /* "EOIE" */
48+ #define CACHE_EXT_INDEXENTRYOFFSETTABLE 0x49454F54 /* "IEOT" */
4849
4950/* changes that can be kept in $GIT_DIR/index (basically all extensions) */
5051#define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
@@ -1696,6 +1697,7 @@ static int read_index_extension(struct index_state *istate,
16961697 read_fsmonitor_extension (istate , data , sz );
16971698 break ;
16981699 case CACHE_EXT_ENDOFINDEXENTRIES :
1700+ case CACHE_EXT_INDEXENTRYOFFSETTABLE :
16991701 /* already handled in do_read_index() */
17001702 break ;
17011703 default :
@@ -1888,6 +1890,23 @@ static size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
18881890 return ondisk_size + entries * per_entry ;
18891891}
18901892
1893+ struct index_entry_offset
1894+ {
1895+ /* starting byte offset into index file, count of index entries in this block */
1896+ int offset , nr ;
1897+ };
1898+
1899+ struct index_entry_offset_table
1900+ {
1901+ int nr ;
1902+ struct index_entry_offset entries [FLEX_ARRAY ];
1903+ };
1904+
1905+ #ifndef NO_PTHREADS
1906+ static struct index_entry_offset_table * read_ieot_extension (const char * mmap , size_t mmap_size , size_t offset );
1907+ static void write_ieot_extension (struct strbuf * sb , struct index_entry_offset_table * ieot );
1908+ #endif
1909+
18911910static size_t read_eoie_extension (const char * mmap , size_t mmap_size );
18921911static void write_eoie_extension (struct strbuf * sb , git_hash_ctx * eoie_context , size_t offset );
18931912
@@ -1929,6 +1948,15 @@ static void *load_index_extensions(void *_data)
19291948 return NULL ;
19301949}
19311950
1951+ /*
1952+ * Mostly randomly chosen maximum thread counts: we
1953+ * cap the parallelism to online_cpus() threads, and we want
1954+ * to have at least 10000 cache entries per thread for it to
1955+ * be worth starting a thread.
1956+ */
1957+
1958+ #define THREAD_COST (10000)
1959+
19321960/* remember to discard_cache() before reading a different cache! */
19331961int do_read_index (struct index_state * istate , const char * path , int must_exist )
19341962{
@@ -2521,6 +2549,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
25212549 struct strbuf previous_name_buf = STRBUF_INIT , * previous_name ;
25222550 int drop_cache_tree = istate -> drop_cache_tree ;
25232551 off_t offset ;
2552+ int ieot_blocks = 1 ;
2553+ struct index_entry_offset_table * ieot = NULL ;
2554+ int nr , nr_threads ;
25242555
25252556 for (i = removed = extended = 0 ; i < entries ; i ++ ) {
25262557 if (cache [i ]-> ce_flags & CE_REMOVE )
@@ -2554,10 +2585,44 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
25542585 if (ce_write (& c , newfd , & hdr , sizeof (hdr )) < 0 )
25552586 return -1 ;
25562587
2588+ #ifndef NO_PTHREADS
2589+ nr_threads = git_config_get_index_threads ();
2590+ if (nr_threads != 1 ) {
2591+ int ieot_blocks , cpus ;
2592+
2593+ /*
2594+ * ensure default number of ieot blocks maps evenly to the
2595+ * default number of threads that will process them leaving
2596+ * room for the thread to load the index extensions.
2597+ */
2598+ if (!nr_threads ) {
2599+ ieot_blocks = istate -> cache_nr / THREAD_COST ;
2600+ cpus = online_cpus ();
2601+ if (ieot_blocks > cpus - 1 )
2602+ ieot_blocks = cpus - 1 ;
2603+ } else {
2604+ ieot_blocks = nr_threads ;
2605+ }
2606+
2607+ /*
2608+ * no reason to write out the IEOT extension if we don't
2609+ * have enough blocks to utilize multi-threading
2610+ */
2611+ if (ieot_blocks > 1 ) {
2612+ ieot = xcalloc (1 , sizeof (struct index_entry_offset_table )
2613+ + (ieot_blocks * sizeof (struct index_entry_offset )));
2614+ ieot_blocks = DIV_ROUND_UP (entries , ieot_blocks );
2615+ }
2616+ }
2617+ #endif
2618+
25572619 offset = lseek (newfd , 0 , SEEK_CUR );
2558- if (offset < 0 )
2620+ if (offset < 0 ) {
2621+ free (ieot );
25592622 return -1 ;
2623+ }
25602624 offset += write_buffer_len ;
2625+ nr = 0 ;
25612626 previous_name = (hdr_version == 4 ) ? & previous_name_buf : NULL ;
25622627
25632628 for (i = 0 ; i < entries ; i ++ ) {
@@ -2579,24 +2644,74 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
25792644
25802645 drop_cache_tree = 1 ;
25812646 }
2647+ if (ieot && i && (i % ieot_blocks == 0 )) {
2648+ ieot -> entries [ieot -> nr ].nr = nr ;
2649+ ieot -> entries [ieot -> nr ].offset = offset ;
2650+ ieot -> nr ++ ;
2651+ /*
2652+ * If we have a V4 index, set the first byte to an invalid
2653+ * character to ensure there is nothing common with the previous
2654+ * entry
2655+ */
2656+ if (previous_name )
2657+ previous_name -> buf [0 ] = 0 ;
2658+ nr = 0 ;
2659+ offset = lseek (newfd , 0 , SEEK_CUR );
2660+ if (offset < 0 ) {
2661+ free (ieot );
2662+ return -1 ;
2663+ }
2664+ offset += write_buffer_len ;
2665+ }
25822666 if (ce_write_entry (& c , newfd , ce , previous_name , (struct ondisk_cache_entry * )& ondisk ) < 0 )
25832667 err = -1 ;
25842668
25852669 if (err )
25862670 break ;
2671+ nr ++ ;
2672+ }
2673+ if (ieot && nr ) {
2674+ ieot -> entries [ieot -> nr ].nr = nr ;
2675+ ieot -> entries [ieot -> nr ].offset = offset ;
2676+ ieot -> nr ++ ;
25872677 }
25882678 strbuf_release (& previous_name_buf );
25892679
2590- if (err )
2680+ if (err ) {
2681+ free (ieot );
25912682 return err ;
2683+ }
25922684
25932685 /* Write extension data here */
25942686 offset = lseek (newfd , 0 , SEEK_CUR );
2595- if (offset < 0 )
2687+ if (offset < 0 ) {
2688+ free (ieot );
25962689 return -1 ;
2690+ }
25972691 offset += write_buffer_len ;
25982692 the_hash_algo -> init_fn (& eoie_c );
25992693
2694+ /*
2695+ * Lets write out CACHE_EXT_INDEXENTRYOFFSETTABLE first so that we
2696+ * can minimize the number of extensions we have to scan through to
2697+ * find it during load. Write it out regardless of the
2698+ * strip_extensions parameter as we need it when loading the shared
2699+ * index.
2700+ */
2701+ #ifndef NO_PTHREADS
2702+ if (ieot ) {
2703+ struct strbuf sb = STRBUF_INIT ;
2704+
2705+ write_ieot_extension (& sb , ieot );
2706+ err = write_index_ext_header (& c , & eoie_c , newfd , CACHE_EXT_INDEXENTRYOFFSETTABLE , sb .len ) < 0
2707+ || ce_write (& c , newfd , sb .buf , sb .len ) < 0 ;
2708+ strbuf_release (& sb );
2709+ free (ieot );
2710+ if (err )
2711+ return -1 ;
2712+ }
2713+ #endif
2714+
26002715 if (!strip_extensions && istate -> split_index ) {
26012716 struct strbuf sb = STRBUF_INIT ;
26022717
@@ -3180,3 +3295,78 @@ static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context,
31803295 the_hash_algo -> final_fn (hash , eoie_context );
31813296 strbuf_add (sb , hash , the_hash_algo -> rawsz );
31823297}
3298+
3299+ #ifndef NO_PTHREADS
3300+ #define IEOT_VERSION (1)
3301+
3302+ static struct index_entry_offset_table * read_ieot_extension (const char * mmap , size_t mmap_size , size_t offset )
3303+ {
3304+ const char * index = NULL ;
3305+ uint32_t extsize , ext_version ;
3306+ struct index_entry_offset_table * ieot ;
3307+ int i , nr ;
3308+
3309+ /* find the IEOT extension */
3310+ if (!offset )
3311+ return NULL ;
3312+ while (offset <= mmap_size - the_hash_algo -> rawsz - 8 ) {
3313+ extsize = get_be32 (mmap + offset + 4 );
3314+ if (CACHE_EXT ((mmap + offset )) == CACHE_EXT_INDEXENTRYOFFSETTABLE ) {
3315+ index = mmap + offset + 4 + 4 ;
3316+ break ;
3317+ }
3318+ offset += 8 ;
3319+ offset += extsize ;
3320+ }
3321+ if (!index )
3322+ return NULL ;
3323+
3324+ /* validate the version is IEOT_VERSION */
3325+ ext_version = get_be32 (index );
3326+ if (ext_version != IEOT_VERSION ) {
3327+ error ("invalid IEOT version %d" , ext_version );
3328+ return NULL ;
3329+ }
3330+ index += sizeof (uint32_t );
3331+
3332+ /* extension size - version bytes / bytes per entry */
3333+ nr = (extsize - sizeof (uint32_t )) / (sizeof (uint32_t ) + sizeof (uint32_t ));
3334+ if (!nr ) {
3335+ error ("invalid number of IEOT entries %d" , nr );
3336+ return NULL ;
3337+ }
3338+ ieot = xmalloc (sizeof (struct index_entry_offset_table )
3339+ + (nr * sizeof (struct index_entry_offset )));
3340+ ieot -> nr = nr ;
3341+ for (i = 0 ; i < nr ; i ++ ) {
3342+ ieot -> entries [i ].offset = get_be32 (index );
3343+ index += sizeof (uint32_t );
3344+ ieot -> entries [i ].nr = get_be32 (index );
3345+ index += sizeof (uint32_t );
3346+ }
3347+
3348+ return ieot ;
3349+ }
3350+
3351+ static void write_ieot_extension (struct strbuf * sb , struct index_entry_offset_table * ieot )
3352+ {
3353+ uint32_t buffer ;
3354+ int i ;
3355+
3356+ /* version */
3357+ put_be32 (& buffer , IEOT_VERSION );
3358+ strbuf_add (sb , & buffer , sizeof (uint32_t ));
3359+
3360+ /* ieot */
3361+ for (i = 0 ; i < ieot -> nr ; i ++ ) {
3362+
3363+ /* offset */
3364+ put_be32 (& buffer , ieot -> entries [i ].offset );
3365+ strbuf_add (sb , & buffer , sizeof (uint32_t ));
3366+
3367+ /* count */
3368+ put_be32 (& buffer , ieot -> entries [i ].nr );
3369+ strbuf_add (sb , & buffer , sizeof (uint32_t ));
3370+ }
3371+ }
3372+ #endif
0 commit comments