Skip to content

Commit 1930ea4

Browse files
committed
[LIT] Workaround the 60 processed limit on Windows
Python multiprocessing is limited to 60 workers at most: https://github.com/python/cpython/blob/6bc65c30ff1fd0b581a2c93416496fc720bc442c/Lib/concurrent/futures/process.py#L669-L672 The limit being per thread pool, we can work around it by using multiple pools on windows when we want to actually use more workers.
1 parent 9277bcd commit 1930ea4

File tree

2 files changed

+43
-15
lines changed

2 files changed

+43
-15
lines changed

llvm/utils/lit/lit/run.py

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,25 +72,58 @@ def _execute(self, deadline):
7272
if v is not None
7373
}
7474

75-
pool = multiprocessing.Pool(
76-
self.workers, lit.worker.initialize, (self.lit_config, semaphores)
75+
# Windows has a limit of 60 workers per pool, so we need to use multiple pools
76+
# if we have more than 60 workers requested
77+
max_workers_per_pool = 60 if os.name == "nt" else self.workers
78+
num_pools = max(
79+
1, (self.workers + max_workers_per_pool - 1) // max_workers_per_pool
7780
)
81+
workers_per_pool = min(self.workers, max_workers_per_pool)
7882

79-
async_results = [
80-
pool.apply_async(
81-
lit.worker.execute, args=[test], callback=self.progress_callback
83+
if num_pools > 1:
84+
self.lit_config.note(
85+
"Using %d pools with %d workers each (Windows worker limit workaround)"
86+
% (num_pools, workers_per_pool)
8287
)
83-
for test in self.tests
84-
]
85-
pool.close()
88+
89+
# Create multiple pools
90+
pools = []
91+
for i in range(num_pools):
92+
pool = multiprocessing.Pool(
93+
workers_per_pool, lit.worker.initialize, (self.lit_config, semaphores)
94+
)
95+
pools.append(pool)
96+
97+
# Distribute tests across pools
98+
tests_per_pool = (len(self.tests) + num_pools - 1) // num_pools
99+
async_results = []
100+
101+
for pool_idx, pool in enumerate(pools):
102+
start_idx = pool_idx * tests_per_pool
103+
end_idx = min(start_idx + tests_per_pool, len(self.tests))
104+
pool_tests = self.tests[start_idx:end_idx]
105+
106+
for test in pool_tests:
107+
ar = pool.apply_async(
108+
lit.worker.execute, args=[test], callback=self.progress_callback
109+
)
110+
async_results.append(ar)
111+
112+
# Close all pools
113+
for pool in pools:
114+
pool.close()
86115

87116
try:
88117
self._wait_for(async_results, deadline)
89118
except:
90-
pool.terminate()
119+
# Terminate all pools on exception
120+
for pool in pools:
121+
pool.terminate()
91122
raise
92123
finally:
93-
pool.join()
124+
# Join all pools
125+
for pool in pools:
126+
pool.join()
94127

95128
def _wait_for(self, async_results, deadline):
96129
timeout = deadline - time.time()

llvm/utils/lit/lit/util.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,6 @@ def usable_core_count():
113113
except AttributeError:
114114
n = os.cpu_count() or 1
115115

116-
# On Windows with more than 60 processes, multiprocessing's call to
117-
# _winapi.WaitForMultipleObjects() prints an error and lit hangs.
118-
if platform.system() == "Windows":
119-
return min(n, 60)
120-
121116
return n
122117

123118
def abs_path_preserve_drive(path):

0 commit comments

Comments
 (0)