summaryrefslogtreecommitdiffstats
path: root/subcmds/start.py
diff options
context:
space:
mode:
authorKuang-che Wu <kcwu@google.com>2024-10-22 21:04:41 +0800
committerLUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-10-23 23:34:34 +0000
commit8da4861b3860c505e39341b4135c21f67569e4d8 (patch)
tree6f300266c91322df0e61953b84381e1f403074a5 /subcmds/start.py
parent39ffd9977e2f6cb1ca1757e59173fc93e0eab72c (diff)
downloadgit-repo-8da4861b3860c505e39341b4135c21f67569e4d8.tar.gz
subcmds: reduce multiprocessing serialization overhead
Follow the same approach as 39ffd9977e to reduce serialization overhead. Below benchmarks are tested with 2.7k projects on my workstation (warm cache). git tracing is disabled for benchmark. (seconds) | v2.48 | v2.48 | this CL | this CL | | -j32 | | -j32 ----------------------------------------------------------- with clean tree state: branches (none) | 5.6 | 5.9 | 1.0 | 0.9 status (clean) | 21.3 | 9.4 | 19.4 | 4.7 diff (none) | 7.6 | 7.2 | 5.7 | 2.2 prune (none) | 5.7 | 6.1 | 1.3 | 1.2 abandon (none) | 19.4 | 18.6 | 0.9 | 0.8 upload (none) | 19.7 | 18.7 | 0.9 | 0.8 forall -c true | 7.5 | 7.6 | 0.6 | 0.6 forall -c "git log -1" | 11.3 | 11.1 | 0.6 | 0.6 with branches: start BRANCH --all | 21.9 | 20.3 | 13.6 | 2.6 checkout BRANCH | 29.1 | 27.8 | 1.1 | 1.0 branches (2) | 28.0 | 28.6 | 1.5 | 1.3 abandon BRANCH | 29.2 | 27.5 | 9.7 | 2.2 Bug: b/371638995 Change-Id: I53989a3d1e43063587b3f52f852b1c2c56b49412 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/440221 Reviewed-by: Josip Sokcevic <sokcevic@google.com> Tested-by: Kuang-che Wu <kcwu@google.com> Commit-Queue: Kuang-che Wu <kcwu@google.com>
Diffstat (limited to 'subcmds/start.py')
-rw-r--r--subcmds/start.py40
1 files changed, 25 insertions, 15 deletions
diff --git a/subcmds/start.py b/subcmds/start.py
index 56008f42..6dca7e4e 100644
--- a/subcmds/start.py
+++ b/subcmds/start.py
@@ -21,7 +21,6 @@ from error import RepoExitError
21from git_command import git 21from git_command import git
22from git_config import IsImmutable 22from git_config import IsImmutable
23from progress import Progress 23from progress import Progress
24from project import Project
25from repo_logging import RepoLogger 24from repo_logging import RepoLogger
26 25
27 26
@@ -29,7 +28,7 @@ logger = RepoLogger(__file__)
29 28
30 29
31class ExecuteOneResult(NamedTuple): 30class ExecuteOneResult(NamedTuple):
32 project: Project 31 project_idx: int
33 error: Exception 32 error: Exception
34 33
35 34
@@ -80,18 +79,20 @@ revision specified in the manifest.
80 if not git.check_ref_format("heads/%s" % nb): 79 if not git.check_ref_format("heads/%s" % nb):
81 self.OptionParser.error("'%s' is not a valid name" % nb) 80 self.OptionParser.error("'%s' is not a valid name" % nb)
82 81
83 def _ExecuteOne(self, revision, nb, project): 82 @classmethod
83 def _ExecuteOne(cls, revision, nb, default_revisionExpr, project_idx):
84 """Start one project.""" 84 """Start one project."""
85 # If the current revision is immutable, such as a SHA1, a tag or 85 # If the current revision is immutable, such as a SHA1, a tag or
86 # a change, then we can't push back to it. Substitute with 86 # a change, then we can't push back to it. Substitute with
87 # dest_branch, if defined; or with manifest default revision instead. 87 # dest_branch, if defined; or with manifest default revision instead.
88 branch_merge = "" 88 branch_merge = ""
89 error = None 89 error = None
90 project = cls.get_parallel_context()["projects"][project_idx]
90 if IsImmutable(project.revisionExpr): 91 if IsImmutable(project.revisionExpr):
91 if project.dest_branch: 92 if project.dest_branch:
92 branch_merge = project.dest_branch 93 branch_merge = project.dest_branch
93 else: 94 else:
94 branch_merge = self.manifest.default.revisionExpr 95 branch_merge = default_revisionExpr
95 96
96 try: 97 try:
97 project.StartBranch( 98 project.StartBranch(
@@ -100,7 +101,7 @@ revision specified in the manifest.
100 except Exception as e: 101 except Exception as e:
101 logger.error("error: unable to checkout %s: %s", project.name, e) 102 logger.error("error: unable to checkout %s: %s", project.name, e)
102 error = e 103 error = e
103 return ExecuteOneResult(project, error) 104 return ExecuteOneResult(project_idx, error)
104 105
105 def Execute(self, opt, args): 106 def Execute(self, opt, args):
106 nb = args[0] 107 nb = args[0]
@@ -120,19 +121,28 @@ revision specified in the manifest.
120 def _ProcessResults(_pool, pm, results): 121 def _ProcessResults(_pool, pm, results):
121 for result in results: 122 for result in results:
122 if result.error: 123 if result.error:
123 err_projects.append(result.project) 124 project = all_projects[result.project_idx]
125 err_projects.append(project)
124 err.append(result.error) 126 err.append(result.error)
125 pm.update(msg="") 127 pm.update(msg="")
126 128
127 self.ExecuteInParallel( 129 with self.ParallelContext():
128 opt.jobs, 130 self.get_parallel_context()["projects"] = all_projects
129 functools.partial(self._ExecuteOne, opt.revision, nb), 131 self.ExecuteInParallel(
130 all_projects, 132 opt.jobs,
131 callback=_ProcessResults, 133 functools.partial(
132 output=Progress( 134 self._ExecuteOne,
133 f"Starting {nb}", len(all_projects), quiet=opt.quiet 135 opt.revision,
134 ), 136 nb,
135 ) 137 self.manifest.default.revisionExpr,
138 ),
139 range(len(all_projects)),
140 callback=_ProcessResults,
141 output=Progress(
142 f"Starting {nb}", len(all_projects), quiet=opt.quiet
143 ),
144 chunksize=1,
145 )
136 146
137 if err_projects: 147 if err_projects:
138 for p in err_projects: 148 for p in err_projects: