diff --git a/Documentation/config/checkout.txt b/Documentation/config/checkout.txt index 2cddf7b4b4..bfbca90f0e 100644 --- a/Documentation/config/checkout.txt +++ b/Documentation/config/checkout.txt @@ -21,3 +21,24 @@ checkout.guess:: Provides the default value for the `--guess` or `--no-guess` option in `git checkout` and `git switch`. See linkgit:git-switch[1] and linkgit:git-checkout[1]. + +checkout.workers:: + The number of parallel workers to use when updating the working tree. + The default is one, i.e. sequential execution. If set to a value less + than one, Git will use as many workers as the number of logical cores + available. This setting and `checkout.thresholdForParallelism` affect + all commands that perform checkout. E.g. checkout, clone, reset, + sparse-checkout, etc. ++ +Note: parallel checkout usually delivers better performance for repositories +located on SSDs or over NFS. For repositories on spinning disks and/or machines +with a small number of cores, the default sequential checkout often performs +better. The size and compression level of a repository might also influence how +well the parallel version performs. + +checkout.thresholdForParallelism:: + When running parallel checkout with a small number of files, the cost + of subprocess spawning and inter-process communication might outweigh + the parallelization gains. This setting allows to define the minimum + number of files for which parallel checkout should be attempted. The + default is 100. diff --git a/parallel-checkout.c b/parallel-checkout.c index 836154fec6..3cc2028861 100644 --- a/parallel-checkout.c +++ b/parallel-checkout.c @@ -1,10 +1,12 @@ #include "cache.h" +#include "config.h" #include "entry.h" #include "parallel-checkout.h" #include "pkt-line.h" #include "run-command.h" #include "sigchain.h" #include "streaming.h" +#include "thread-utils.h" struct pc_worker { struct child_process cp; @@ -24,6 +26,20 @@ enum pc_status parallel_checkout_status(void) return parallel_checkout.status; } +static const int DEFAULT_THRESHOLD_FOR_PARALLELISM = 100; +static const int DEFAULT_NUM_WORKERS = 1; + +void get_parallel_checkout_configs(int *num_workers, int *threshold) +{ + if (git_config_get_int("checkout.workers", num_workers)) + *num_workers = DEFAULT_NUM_WORKERS; + else if (*num_workers < 1) + *num_workers = online_cpus(); + + if (git_config_get_int("checkout.thresholdForParallelism", threshold)) + *threshold = DEFAULT_THRESHOLD_FOR_PARALLELISM; +} + void init_parallel_checkout(void) { if (parallel_checkout.status != PC_UNINITIALIZED) @@ -584,11 +600,9 @@ static void write_items_sequentially(struct checkout *state) write_pc_item(¶llel_checkout.items[i], state); } -static const int DEFAULT_NUM_WORKERS = 2; - -int run_parallel_checkout(struct checkout *state) +int run_parallel_checkout(struct checkout *state, int num_workers, int threshold) { - int ret, num_workers = DEFAULT_NUM_WORKERS; + int ret; if (parallel_checkout.status != PC_ACCEPTING_ENTRIES) BUG("cannot run parallel checkout: uninitialized or already running"); @@ -598,7 +612,7 @@ int run_parallel_checkout(struct checkout *state) if (parallel_checkout.nr < num_workers) num_workers = parallel_checkout.nr; - if (num_workers <= 1) { + if (num_workers <= 1 || parallel_checkout.nr < threshold) { write_items_sequentially(state); } else { struct pc_worker *workers = setup_workers(state, num_workers); diff --git a/parallel-checkout.h b/parallel-checkout.h index ec58716519..2a68ab954d 100644 --- a/parallel-checkout.h +++ b/parallel-checkout.h @@ -17,6 +17,7 @@ enum pc_status { }; enum pc_status parallel_checkout_status(void); +void get_parallel_checkout_configs(int *num_workers, int *threshold); /* * Put parallel checkout into the PC_ACCEPTING_ENTRIES state. Should be used @@ -31,8 +32,12 @@ void init_parallel_checkout(void); */ int enqueue_checkout(struct cache_entry *ce, struct conv_attrs *ca); -/* Write all the queued entries, returning 0 on success.*/ -int run_parallel_checkout(struct checkout *state); +/* + * Write all the queued entries, returning 0 on success. If the number of + * entries is smaller than the specified threshold, the operation is performed + * sequentially. + */ +int run_parallel_checkout(struct checkout *state, int num_workers, int threshold); /**************************************************************** * Interface with checkout--worker diff --git a/unpack-trees.c b/unpack-trees.c index f0430d458d..0669748f21 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -399,7 +399,7 @@ static int check_updates(struct unpack_trees_options *o, int errs = 0; struct progress *progress; struct checkout state = CHECKOUT_INIT; - int i; + int i, pc_workers, pc_threshold; trace_performance_enter(); state.force = 1; @@ -465,8 +465,11 @@ static int check_updates(struct unpack_trees_options *o, oid_array_clear(&to_fetch); } + get_parallel_checkout_configs(&pc_workers, &pc_threshold); + enable_delayed_checkout(&state); - init_parallel_checkout(); + if (pc_workers > 1) + init_parallel_checkout(); for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; @@ -480,7 +483,8 @@ static int check_updates(struct unpack_trees_options *o, } } stop_progress(&progress); - errs |= run_parallel_checkout(&state); + if (pc_workers > 1) + errs |= run_parallel_checkout(&state, pc_workers, pc_threshold); errs |= finish_delayed_checkout(&state, NULL); git_attr_set_direction(GIT_ATTR_CHECKIN);