@@ -16,3 +16,19 @@ will checkout the '<something>' branch on another remote,
and by linkgit:git-worktree[1] when 'git worktree add' refers to a
remote branch. This setting might be used for other checkout-like
commands or functionality in the future.
+
+checkout.workers::
+ The number of worker processes to use when updating the working tree.
+ If unset (or set to a value less than one), Git will use as many
+ workers as the number of logical cores available. One means sequential
+ execution. This and the checkout.workersThreshold settings affect all
+ commands which perform checkout. E.g. checkout, switch, clone,
+ sparse-checkout, read-tree, etc.
+
+checkout.workersThreshold::
+ If set to a positive number, parallel checkout will not be attempted
+ when the number of files to be updated is less than the defined limit.
+ When set to a negative number or unset, defaults to 0. The reasoning
+ behind this config is that, when modifying a small number of files, a
+ sequential execution might be faster, as it avoids the cost of spawning
+ subprocesses and inter-process communication.
@@ -4,6 +4,8 @@
#include "pkt-line.h"
#include "run-command.h"
#include "streaming.h"
+#include "thread-utils.h"
+#include "config.h"
struct parallel_checkout {
struct checkout_item *items;
@@ -18,6 +20,19 @@ enum pc_status parallel_checkout_status(void)
return pc_status;
}
+#define DEFAULT_WORKERS_THRESHOLD 0
+
+void get_parallel_checkout_configs(int *num_workers, int *threshold)
+{
+ if (git_config_get_int("checkout.workers", num_workers) ||
+ *num_workers < 1)
+ *num_workers = online_cpus();
+
+ if (git_config_get_int("checkout.workersThreshold", threshold) ||
+ *threshold < 0)
+ *threshold = DEFAULT_WORKERS_THRESHOLD;
+}
+
void init_parallel_checkout(void)
{
if (parallel_checkout)
@@ -480,22 +495,23 @@ static int run_checkout_sequentially(struct checkout *state)
return handle_results(state);
}
-static const int workers_threshold = 0;
-
-int run_parallel_checkout(struct checkout *state)
+int run_parallel_checkout(struct checkout *state, int num_workers, int threshold)
{
- int num_workers = online_cpus();
int ret = 0;
struct child_process *workers;
if (!parallel_checkout)
BUG("cannot run parallel checkout: not initialized yet");
+ if (num_workers < 1)
+ BUG("invalid number of workers for run_parallel_checkout: %d",
+ num_workers);
+
pc_status = PC_RUNNING;
if (parallel_checkout->nr == 0) {
goto done;
- } else if (parallel_checkout->nr < workers_threshold || num_workers == 1) {
+ } else if (parallel_checkout->nr < threshold || num_workers == 1) {
ret = run_checkout_sequentially(state);
goto done;
}
@@ -18,6 +18,9 @@ enum pc_status {
enum pc_status parallel_checkout_status(void);
void init_parallel_checkout(void);
+/* Reads the checkout.workers and checkout.workersThreshold settings. */
+void get_parallel_checkout_configs(int *num_workers, int *threshold);
+
/*
* Return -1 if parallel checkout is currently not enabled or if the entry is
* not eligible for parallel checkout. Otherwise, enqueue the entry for later
@@ -25,8 +28,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 below the specified threshold, the operation is performed
+ * sequentially.
+ */
+int run_parallel_checkout(struct checkout *state, int num_workers, int threshold);
/****************************************************************
* Interface with checkout--helper
@@ -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;
@@ -462,8 +462,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];
@@ -477,7 +480,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);
Add the checkout.workers and checkout.workerThreshold settings, which allow users to configure and/or disable the parallel checkout feature as desired. The first setting defines the number of workers and the second defines the minimum number of entries to attempt parallel checkout. Co-authored-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Matheus Tavares <matheus.bernardino@usp.br> --- I still have to evaluate what is the best default value for checkout.workersThreshold. For now, I used 0 so that the test suite uses parallel-checkout by default, exercising the new code. I'm open to suggestions on how we can improve testing for it, once checkout.workersThreshold is no longer 0. Note: the default number of workers can probably be better calculated as well, multiplying the number of cores by some factor. My machine, for example, has 8 logical cores but 10 workers leads to the fastest execution. Documentation/config/checkout.txt | 16 ++++++++++++++++ parallel-checkout.c | 26 +++++++++++++++++++++----- parallel-checkout.h | 11 +++++++++-- unpack-trees.c | 10 +++++++--- 4 files changed, 53 insertions(+), 10 deletions(-)