From 88c4e69802b26780007ecaf8fc3045c342660114 Mon Sep 17 00:00:00 2001 From: Yiding Jia Date: Tue, 5 Jul 2011 11:06:46 -0700 Subject: [PATCH] Parallelize calls for ArcanistGitAPI::getWorkingCopyStatus Summary: executes the calls to git in parallel to improve startup performance of arc lint and possibly other commands Gets about a 2~3x speedup when repo is in buffer cache, with 4 cores. Test Plan: arc linted a repo with unstaged, untracked, staged, and committed changes, see that the same files were linted. Reviewed By: jungejason Reviewers: jungejason, epriestley Commenters: epriestley CC: aran, jungejason, epriestley Differential Revision: 594 --- src/repository/api/git/ArcanistGitAPI.php | 49 ++++++++++++++++------- src/repository/api/git/__init__.php | 1 + 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/repository/api/git/ArcanistGitAPI.php b/src/repository/api/git/ArcanistGitAPI.php index e69f7785..13570a46 100644 --- a/src/repository/api/git/ArcanistGitAPI.php +++ b/src/repository/api/git/ArcanistGitAPI.php @@ -183,17 +183,47 @@ class ArcanistGitAPI extends ArcanistRepositoryAPI { $options = $this->getDiffBaseOptions(); + // -- parallelize these slow cpu bound git calls. + // Find committed changes. - list($stdout) = execx( + $committed_future = new ExecFuture( "(cd %s; git diff {$options} --raw %s --)", $this->getPath(), $this->getRelativeCommit()); - $files = $this->parseGitStatus($stdout); // Find uncommitted changes. - list($stdout) = execx( + $uncommitted_future = new ExecFuture( "(cd %s; git diff {$options} --raw HEAD --)", $this->getPath()); + + // Untracked files + $untracked_future = new ExecFuture( + '(cd %s; git ls-files --others --exclude-standard)', + $this->getPath()); + + // TODO: This doesn't list unstaged adds. It's not clear how to get that + // list other than "git status --porcelain" and then parsing it. :/ + + // Unstaged changes + $unstaged_future = new ExecFuture( + '(cd %s; git ls-files -m)', + $this->getPath()); + + $futures = array( + $committed_future, + $uncommitted_future, + $untracked_future, + $unstaged_future + ); + Futures($futures)->resolveAll(); + + + // -- read back and process the results + + list($stdout, $stderr) = $committed_future->resolvex(); + $files = $this->parseGitStatus($stdout); + + list($stdout, $stderr) = $uncommitted_future->resolvex(); $uncommitted_files = $this->parseGitStatus($stdout); foreach ($uncommitted_files as $path => $mask) { $mask |= self::FLAG_UNCOMMITTED; @@ -203,10 +233,7 @@ class ArcanistGitAPI extends ArcanistRepositoryAPI { $files[$path] |= $mask; } - // Find untracked files. - list($stdout) = execx( - '(cd %s; git ls-files --others --exclude-standard)', - $this->getPath()); + list($stdout, $stderr) = $untracked_future->resolvex(); $stdout = rtrim($stdout, "\n"); if (strlen($stdout)) { $stdout = explode("\n", $stdout); @@ -215,13 +242,7 @@ class ArcanistGitAPI extends ArcanistRepositoryAPI { } } - // TODO: This doesn't list unstaged adds. It's not clear how to get that - // list other than "git status --porcelain" and then parsing it. :/ - - // Find unstaged changes. - list($stdout) = execx( - '(cd %s; git ls-files -m)', - $this->getPath()); + list($stdout, $stderr) = $unstaged_future->resolvex(); $stdout = rtrim($stdout, "\n"); if (strlen($stdout)) { $stdout = explode("\n", $stdout); diff --git a/src/repository/api/git/__init__.php b/src/repository/api/git/__init__.php index 6f6dae56..c225acc7 100644 --- a/src/repository/api/git/__init__.php +++ b/src/repository/api/git/__init__.php @@ -8,6 +8,7 @@ phutil_require_module('arcanist', 'repository/api/base'); +phutil_require_module('phutil', 'future'); phutil_require_module('phutil', 'future/exec'); phutil_require_module('phutil', 'utils');