Skip to content

Commit 2af9664

Browse files
committed
Merge branch 'lt/preload-lstat'
* lt/preload-lstat: Fix index preloading for racy dirty case Add cache preload facility
2 parents 98cdf78 + 7c4ea59 commit 2af9664

File tree

9 files changed

+122
-10
lines changed

9 files changed

+122
-10
lines changed

Documentation/config.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,15 @@ data writes properly, but can be useful for filesystems that do not use
413413
journalling (traditional UNIX filesystems) or that only journal metadata
414414
and not file contents (OS X's HFS+, or Linux ext3 with "data=writeback").
415415

416+
core.preloadindex::
417+
Enable parallel index preload for operations like 'git diff'
418+
+
419+
This can speed up operations like 'git diff' and 'git status' especially
420+
on filesystems like NFS that have weak caching semantics and thus
421+
relatively high IO latencies. With this set to 'true', git will do the
422+
index comparison to the filesystem data in parallel, allowing
423+
overlapping IO's.
424+
416425
alias.*::
417426
Command aliases for the linkgit:git[1] command wrapper - e.g.
418427
after defining "alias.last = cat-file commit HEAD", the invocation

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ LIB_OBJS += write_or_die.o
496496
LIB_OBJS += ws.o
497497
LIB_OBJS += wt-status.o
498498
LIB_OBJS += xdiff-interface.o
499+
LIB_OBJS += preload-index.o
499500

500501
BUILTIN_OBJS += builtin-add.o
501502
BUILTIN_OBJS += builtin-annotate.o

builtin-commit.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,18 +225,18 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
225225

226226
if (interactive) {
227227
interactive_add(argc, argv, prefix);
228-
if (read_cache() < 0)
228+
if (read_cache_preload(NULL) < 0)
229229
die("index file corrupt");
230230
commit_style = COMMIT_AS_IS;
231231
return get_index_file();
232232
}
233233

234-
if (read_cache() < 0)
235-
die("index file corrupt");
236-
237234
if (*argv)
238235
pathspec = get_pathspec(prefix, argv);
239236

237+
if (read_cache_preload(pathspec) < 0)
238+
die("index file corrupt");
239+
240240
/*
241241
* Non partial, non as-is commit.
242242
*

builtin-diff-files.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
5959
(rev.diffopt.output_format & DIFF_FORMAT_PATCH))
6060
rev.combine_merges = rev.dense_combined_merges = 1;
6161

62-
if (read_cache() < 0) {
63-
perror("read_cache");
62+
if (read_cache_preload(rev.diffopt.paths) < 0) {
63+
perror("read_cache_preload");
6464
return -1;
6565
}
6666
result = run_diff_files(&rev, options);

builtin-diff.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ static int builtin_diff_index(struct rev_info *revs,
134134
revs->max_count != -1 || revs->min_age != -1 ||
135135
revs->max_age != -1)
136136
usage(builtin_diff_usage);
137-
if (read_cache() < 0) {
138-
perror("read_cache");
137+
if (read_cache_preload(revs->diffopt.paths) < 0) {
138+
perror("read_cache_preload");
139139
return -1;
140140
}
141141
return run_diff_index(revs, cached);
@@ -234,8 +234,8 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
234234
revs->combine_merges = revs->dense_combined_merges = 1;
235235

236236
setup_work_tree();
237-
if (read_cache() < 0) {
238-
perror("read_cache");
237+
if (read_cache_preload(revs->diffopt.paths) < 0) {
238+
perror("read_cache_preload");
239239
return -1;
240240
}
241241
result = run_diff_files(revs, options);

cache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
262262

263263
#define read_cache() read_index(&the_index)
264264
#define read_cache_from(path) read_index_from(&the_index, (path))
265+
#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
265266
#define is_cache_unborn() is_index_unborn(&the_index)
266267
#define read_cache_unmerged() read_index_unmerged(&the_index)
267268
#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
@@ -368,6 +369,7 @@ extern int init_db(const char *template_dir, unsigned int flags);
368369

369370
/* Initialize and use the cache information */
370371
extern int read_index(struct index_state *);
372+
extern int read_index_preload(struct index_state *, const char **pathspec);
371373
extern int read_index_from(struct index_state *, const char *path);
372374
extern int is_index_unborn(struct index_state *);
373375
extern int read_index_unmerged(struct index_state *);
@@ -458,6 +460,7 @@ extern size_t packed_git_limit;
458460
extern size_t delta_base_cache_limit;
459461
extern int auto_crlf;
460462
extern int fsync_object_files;
463+
extern int core_preload_index;
461464

462465
enum safe_crlf {
463466
SAFE_CRLF_FALSE = 0,

config.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,11 @@ static int git_default_core_config(const char *var, const char *value)
490490
return 0;
491491
}
492492

493+
if (!strcmp(var, "core.preloadindex")) {
494+
core_preload_index = git_config_bool(var, value);
495+
return 0;
496+
}
497+
493498
/* Add other config variables here and to Documentation/config.txt. */
494499
return 0;
495500
}

environment.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
4343
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
4444
enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
4545

46+
/* Parallel index stat data preload? */
47+
int core_preload_index = 0;
48+
4649
/* This is set by setup_git_dir_gently() and/or git_default_config() */
4750
char *git_work_tree_cfg;
4851
static char *work_tree;

preload-index.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (C) 2008 Linus Torvalds
3+
*/
4+
#include "cache.h"
5+
#include <pthread.h>
6+
7+
/*
8+
* Mostly randomly chosen maximum thread counts: we
9+
* cap the parallelism to 20 threads, and we want
10+
* to have at least 500 lstat's per thread for it to
11+
* be worth starting a thread.
12+
*/
13+
#define MAX_PARALLEL (20)
14+
#define THREAD_COST (500)
15+
16+
struct thread_data {
17+
pthread_t pthread;
18+
struct index_state *index;
19+
const char **pathspec;
20+
int offset, nr;
21+
};
22+
23+
static void *preload_thread(void *_data)
24+
{
25+
int nr;
26+
struct thread_data *p = _data;
27+
struct index_state *index = p->index;
28+
struct cache_entry **cep = index->cache + p->offset;
29+
30+
nr = p->nr;
31+
if (nr + p->offset > index->cache_nr)
32+
nr = index->cache_nr - p->offset;
33+
34+
do {
35+
struct cache_entry *ce = *cep++;
36+
struct stat st;
37+
38+
if (ce_stage(ce))
39+
continue;
40+
if (ce_uptodate(ce))
41+
continue;
42+
if (!ce_path_match(ce, p->pathspec))
43+
continue;
44+
if (lstat(ce->name, &st))
45+
continue;
46+
if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
47+
continue;
48+
ce_mark_uptodate(ce);
49+
} while (--nr > 0);
50+
return NULL;
51+
}
52+
53+
static void preload_index(struct index_state *index, const char **pathspec)
54+
{
55+
int threads, i, work, offset;
56+
struct thread_data data[MAX_PARALLEL];
57+
58+
if (!core_preload_index)
59+
return;
60+
61+
threads = index->cache_nr / THREAD_COST;
62+
if (threads < 2)
63+
return;
64+
if (threads > MAX_PARALLEL)
65+
threads = MAX_PARALLEL;
66+
offset = 0;
67+
work = (index->cache_nr + threads - 1) / threads;
68+
for (i = 0; i < threads; i++) {
69+
struct thread_data *p = data+i;
70+
p->index = index;
71+
p->pathspec = pathspec;
72+
p->offset = offset;
73+
p->nr = work;
74+
offset += work;
75+
if (pthread_create(&p->pthread, NULL, preload_thread, p))
76+
die("unable to create threaded lstat");
77+
}
78+
for (i = 0; i < threads; i++) {
79+
struct thread_data *p = data+i;
80+
if (pthread_join(p->pthread, NULL))
81+
die("unable to join threaded lstat");
82+
}
83+
}
84+
85+
int read_index_preload(struct index_state *index, const char **pathspec)
86+
{
87+
int retval = read_index(index);
88+
89+
preload_index(index, pathspec);
90+
return retval;
91+
}

0 commit comments

Comments
 (0)