Skip to content

Commit c355f7c

Browse files
vmaksimoDoyleLi
authored andcommitted
[SYCL] Enable parallel execution of llvm-foreach commands under an option (intel#4360)
This improvement should speed up toolchain execution by launching underlying llvm-foreach commands in parallel. The feature is available under `--jobs`/`-j` option that should be passed to the command line of llvm-foreach.
1 parent ebbb121 commit c355f7c

File tree

3 files changed

+125
-25
lines changed

3 files changed

+125
-25
lines changed

llvm/test/tools/llvm-foreach/llvm-foreach-lin.ll

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,45 @@
44
; RUN: echo 'Content of second file' > %t2.tgt
55
; RUN: echo "%t1.tgt" > %t.list
66
; RUN: echo "%t2.tgt" >> %t.list
7-
; RUN: llvm-foreach --in-replace="{}" --in-file-list=%t.list -- echo "{}" > %t.res
7+
; RUN: llvm-foreach --jobs=2 --in-replace="{}" --in-file-list=%t.list -- echo "{}" > %t.res
88
; RUN: FileCheck < %t.res %s
9-
; CHECK: [[FIRST:.+1.tgt]]
10-
; CHECK: [[SECOND:.+2.tgt]]
11-
;
9+
; CHECK-DAG: [[FIRST:.+1.tgt]]
10+
; CHECK-DAG: [[SECOND:.+2.tgt]]
11+
12+
; RUN: llvm-foreach --in-replace="{}" --in-file-list=%t.list -- echo "{}" > %t.res
13+
; RUN: FileCheck < %t.res %s --check-prefix=CHECK-ORDER
14+
; CHECK-ORDER: [[FIRST:.+1.tgt]]
15+
; CHECK-ORDER: [[SECOND:.+2.tgt]]
16+
1217
; RUN: llvm-foreach --in-replace="{}" --out-replace=%t --out-ext=out --in-file-list=%t.list --out-file-list=%t.out.list -- cp "{}" %t
1318
; RUN: FileCheck < %t.out.list %s --check-prefix=CHECK-LIST
19+
; RUN: llvm-foreach --jobs=2 --in-replace="{}" --out-replace=%t --out-ext=out --in-file-list=%t.list --out-file-list=%t.out.list -- cp "{}" %t
20+
; RUN: FileCheck < %t.out.list %s --check-prefix=CHECK-LIST
1421
; CHECK-LIST: [[FIRST:.+\.out]]
1522
; CHECK-LIST: [[SECOND:.+\.out]]
16-
; RUN: llvm-foreach --in-replace="{}" --in-file-list=%t.out.list -- FileCheck --input-file="{}" %s --check-prefix=CHECK-CONTENT
17-
; CHECK-CONTENT: Content of
23+
; RUN: llvm-foreach --in-replace="{}" --in-file-list=%t.out.list -- cat "{}" > %t.order
24+
; RUN: FileCheck < %t.order %s --check-prefix=CHECK-CONTENT
25+
; CHECK-CONTENT: Content of first file
26+
; CHECK-CONTENT-NEXT: Content of second file
1827

1928
; RUN: echo 'something' > %t3.tgt
2029
; RUN: echo 'something again' > %t4.tgt
2130
; RUN: echo "%t3.tgt" > %t1.list
2231
; RUN: echo "%t4.tgt" >> %t1.list
2332
; RUN: llvm-foreach --in-replace="{}" --in-replace="inrep" --in-file-list=%t.list --in-file-list=%t1.list --out-increment="%t_out.prj" -- echo -first-part-of-arg={}.out -first-part-of-arg=inrep.out -another-arg=%t_out.prj > %t1.res
2433
; RUN: FileCheck < %t1.res %s --check-prefix=CHECK-DOUBLE-LISTS
25-
; CHECK-DOUBLE-LISTS: -first-part-of-arg=[[FIRST:.+1.tgt.out]] -first-part-of-arg=[[THIRD:.+3.tgt.out]] -another-arg={{.+}}_out.prj
26-
; CHECK-DOUBLE-LISTS: -first-part-of-arg=[[SECOND:.+2.tgt.out]] -first-part-of-arg=[[FOURTH:.+4.tgt.out]] -another-arg={{.+}}_out.prj_1
34+
; RUN: llvm-foreach --jobs=2 --in-replace="{}" --in-replace="inrep" --in-file-list=%t.list --in-file-list=%t1.list --out-increment="%t_out.prj" -- echo -first-part-of-arg={}.out -first-part-of-arg=inrep.out -another-arg=%t_out.prj > %t1.res
35+
; RUN: FileCheck < %t1.res %s --check-prefix=CHECK-DOUBLE-LISTS
36+
; CHECK-DOUBLE-LISTS-DAG: -first-part-of-arg=[[FIRST:.+1.tgt.out]] -first-part-of-arg=[[THIRD:.+3.tgt.out]] -another-arg={{.+}}_out.prj
37+
; CHECK-DOUBLE-LISTS-DAG: -first-part-of-arg=[[SECOND:.+2.tgt.out]] -first-part-of-arg=[[FOURTH:.+4.tgt.out]] -another-arg={{.+}}_out.prj_1
38+
39+
; RUN: echo "%t1.tgt" > %t2.list
40+
; RUN: echo "%t2.tgt" >> %t2.list
41+
; RUN: echo "%t3.tgt" >> %t2.list
42+
; RUN: echo "%t4.tgt" >> %t2.list
43+
; RUN: llvm-foreach -j 2 --in-replace="{}" --in-file-list=%t2.list -- echo "{}" > %t2.res
44+
; RUN: FileCheck < %t2.res %s --check-prefix=CHECK-PARALLEL-JOBS
45+
; CHECK-PARALLEL-JOBS-DAG: [[FIRST:.+1.tgt]]
46+
; CHECK-PARALLEL-JOBS-DAG: [[SECOND:.+2.tgt]]
47+
; CHECK-PARALLEL-JOBS-DAG: [[THIRD:.+3.tgt]]
48+
; CHECK-PARALLEL-JOBS-DAG: [[FOURTH:.+4.tgt]]

llvm/test/tools/llvm-foreach/llvm-foreach-win.ll

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,45 @@
44
; RUN: echo 'Content of second file' > %t2.tgt
55
; RUN: echo "%t1.tgt" > %t.list
66
; RUN: echo "%t2.tgt" >> %t.list
7-
; RUN: llvm-foreach --in-replace="{}" --in-file-list=%t.list -- echo "{}" > %t.res
7+
; RUN: llvm-foreach --jobs=2 --in-replace="{}" --in-file-list=%t.list -- echo "{}" > %t.res
88
; RUN: FileCheck < %t.res %s
9-
; CHECK: [[FIRST:.+1.tgt]]
10-
; CHECK: [[SECOND:.+2.tgt]]
11-
;
9+
; CHECK-DAG: [[FIRST:.+1.tgt]]
10+
; CHECK-DAG: [[SECOND:.+2.tgt]]
11+
12+
; RUN: llvm-foreach --in-replace="{}" --in-file-list=%t.list -- echo "{}" > %t.res
13+
; RUN: FileCheck < %t.res %s --check-prefix=CHECK-ORDER
14+
; CHECK-ORDER: [[FIRST:.+1.tgt]]
15+
; CHECK-ORDER: [[SECOND:.+2.tgt]]
16+
1217
; RUN: llvm-foreach --in-replace="{}" --out-replace=%t --out-ext=out --in-file-list=%t.list --out-file-list=%t.out.list -- xcopy /y "{}" %t
1318
; RUN: FileCheck < %t.out.list %s --check-prefix=CHECK-LIST
19+
; RUN: llvm-foreach --jobs=2 --in-replace="{}" --out-replace=%t --out-ext=out --in-file-list=%t.list --out-file-list=%t.out.list -- xcopy /y "{}" %t
20+
; RUN: FileCheck < %t.out.list %s --check-prefix=CHECK-LIST
1421
; CHECK-LIST: [[FIRST:.+\.out]]
1522
; CHECK-LIST: [[SECOND:.+\.out]]
16-
; RUN: llvm-foreach --in-replace="{}" --in-file-list=%t.out.list -- FileCheck --input-file="{}" %s --check-prefix=CHECK-CONTENT
17-
; CHECK-CONTENT: Content of
23+
; RUN: llvm-foreach --in-replace="{}" --in-file-list=%t.out.list -- cat "{}" > %t.order
24+
; RUN: FileCheck < %t.order %s --check-prefix=CHECK-CONTENT
25+
; CHECK-CONTENT: Content of first file
26+
; CHECK-CONTENT-NEXT: Content of second file
1827

1928
; RUN: echo 'something' > %t3.tgt
2029
; RUN: echo 'something again' > %t4.tgt
2130
; RUN: echo "%t3.tgt" > %t1.list
2231
; RUN: echo "%t4.tgt" >> %t1.list
2332
; RUN: llvm-foreach --in-replace="{}" --in-replace="inrep" --in-file-list=%t.list --in-file-list=%t1.list --out-increment=%t_out.prj -- echo -first-part-of-arg={}.out -first-part-of-arg=inrep.out -another-arg=%t_out.prj > %t1.res
2433
; RUN: FileCheck < %t1.res %s --check-prefix=CHECK-DOUBLE-LISTS
25-
; CHECK-DOUBLE-LISTS: -first-part-of-arg=[[FIRST:.+1.tgt.out]] -first-part-of-arg=[[THIRD:.+3.tgt.out]] -another-arg={{.+}}_out.prj
26-
; CHECK-DOUBLE-LISTS: -first-part-of-arg=[[SECOND:.+2.tgt.out]] -first-part-of-arg=[[FOURTH:.+4.tgt.out]] -another-arg={{.+}}_out.prj_1
34+
; RUN: llvm-foreach --jobs=2 --in-replace="{}" --in-replace="inrep" --in-file-list=%t.list --in-file-list=%t1.list --out-increment=%t_out.prj -- echo -first-part-of-arg={}.out -first-part-of-arg=inrep.out -another-arg=%t_out.prj > %t1.res
35+
; RUN: FileCheck < %t1.res %s --check-prefix=CHECK-DOUBLE-LISTS
36+
; CHECK-DOUBLE-LISTS-DAG: -first-part-of-arg=[[FIRST:.+1.tgt.out]] -first-part-of-arg=[[THIRD:.+3.tgt.out]] -another-arg={{.+}}_out.prj
37+
; CHECK-DOUBLE-LISTS-DAG: -first-part-of-arg=[[SECOND:.+2.tgt.out]] -first-part-of-arg=[[FOURTH:.+4.tgt.out]] -another-arg={{.+}}_out.prj_1
38+
39+
; RUN: echo "%t1.tgt" > %t2.list
40+
; RUN: echo "%t2.tgt" >> %t2.list
41+
; RUN: echo "%t3.tgt" >> %t2.list
42+
; RUN: echo "%t4.tgt" >> %t2.list
43+
; RUN: llvm-foreach --jobs=2 --in-replace="{}" --in-file-list=%t2.list -- echo "{}" > %t2.res
44+
; RUN: FileCheck < %t2.res %s --check-prefix=CHECK-PARALLEL-JOBS
45+
; CHECK-PARALLEL-JOBS-DAG: [[FIRST:.+1.tgt]]
46+
; CHECK-PARALLEL-JOBS-DAG: [[SECOND:.+2.tgt]]
47+
; CHECK-PARALLEL-JOBS-DAG: [[THIRD:.+3.tgt]]
48+
; CHECK-PARALLEL-JOBS-DAG: [[FOURTH:.+4.tgt]]

llvm/tools/llvm-foreach/llvm-foreach.cpp

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
#include "llvm/Support/Path.h"
1919
#include "llvm/Support/Program.h"
2020
#include "llvm/Support/SystemUtils.h"
21+
#include "llvm/Support/Threading.h"
2122

23+
#include <list>
2224
#include <vector>
2325

2426
using namespace llvm;
@@ -69,6 +71,17 @@ static cl::opt<std::string> OutIncrement{
6971
"pass."),
7072
cl::init(""), cl::value_desc("R")};
7173

74+
static cl::opt<unsigned int> JobsInParallel{
75+
"jobs",
76+
cl::Optional,
77+
cl::init(1),
78+
cl::desc("Specify the number of threads for launching input commands in "
79+
"parallel mode"),
80+
};
81+
82+
static cl::alias JobsInParallelShort{"j", cl::desc("Alias for --jobs"),
83+
cl::aliasopt(JobsInParallel)};
84+
7285
static void error(const Twine &Msg) {
7386
errs() << "llvm-foreach: " << Msg << '\n';
7487
exit(1);
@@ -79,6 +92,32 @@ static void error(std::error_code EC, const Twine &Prefix) {
7992
error(Prefix + ": " + EC.message());
8093
}
8194

95+
// With BlockingWait=false this function just goes through the all
96+
// submitted jobs to check if some of them have finished.
97+
int checkIfJobsAreFinished(std::list<sys::ProcessInfo> &JobsSubmitted,
98+
bool BlockingWait = true) {
99+
std::string ErrMsg;
100+
auto It = JobsSubmitted.begin();
101+
while (It != JobsSubmitted.end()) {
102+
sys::ProcessInfo WaitResult =
103+
sys::Wait(*It, 0, /*WaitUntilTerminates*/ BlockingWait, &ErrMsg);
104+
105+
// Check if the job has finished (PID will be 0 if it's not).
106+
if (!BlockingWait && !WaitResult.Pid) {
107+
It++;
108+
continue;
109+
}
110+
assert(BlockingWait || WaitResult.Pid);
111+
It = JobsSubmitted.erase(It);
112+
113+
if (WaitResult.ReturnCode != 0) {
114+
errs() << "llvm-foreach: " << ErrMsg << '\n';
115+
return WaitResult.ReturnCode;
116+
}
117+
}
118+
return 0;
119+
}
120+
82121
int main(int argc, char **argv) {
83122
cl::ParseCommandLineOptions(
84123
argc, argv,
@@ -160,6 +199,16 @@ int main(int argc, char **argv) {
160199
PrevNumOfLines = FileLists[i].size();
161200
}
162201

202+
if (!JobsInParallel)
203+
error("Number of parallel threads should be a positive integer");
204+
205+
size_t MaxSafeNumThreads = optimal_concurrency().compute_thread_count();
206+
if (JobsInParallel > MaxSafeNumThreads) {
207+
JobsInParallel = MaxSafeNumThreads;
208+
outs() << "llvm-foreach: adjusted number of threads to "
209+
<< MaxSafeNumThreads << " (max safe available).\n";
210+
}
211+
163212
std::error_code EC;
164213
raw_fd_ostream OS{OutputFileList, EC, sys::fs::OpenFlags::OF_None};
165214
if (!OutputFileList.empty())
@@ -170,6 +219,7 @@ int main(int argc, char **argv) {
170219
std::string IncOutArg;
171220
std::vector<std::string> ResInArgs(InReplaceArgs.size());
172221
std::string ResFileList = "";
222+
std::list<sys::ProcessInfo> JobsSubmitted;
173223
for (size_t j = 0; j != FileLists[0].size(); ++j) {
174224
for (size_t i = 0; i < InReplaceArgs.size(); ++i) {
175225
ArgumentReplace CurReplace = InReplaceArgs[i];
@@ -221,17 +271,23 @@ int main(int argc, char **argv) {
221271
Args[OutIncrementArg.ArgNum] = IncOutArg;
222272
}
223273

224-
std::string ErrMsg;
225-
// TODO: Add possibility to execute commands in parallel.
226-
int Result =
227-
sys::ExecuteAndWait(Prog, Args, /*Env=*/None, /*Redirects=*/None,
228-
/*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg);
229-
if (Result != 0) {
230-
errs() << "llvm-foreach: " << ErrMsg << '\n';
231-
Res = Result;
232-
}
274+
// Do not start execution of a new job until previous one(s) are finished,
275+
// if the maximum number of parallel workers is reached.
276+
while (JobsSubmitted.size() == JobsInParallel)
277+
if (int Result =
278+
checkIfJobsAreFinished(JobsSubmitted, /*BlockingWait*/ false))
279+
Res = Result;
280+
281+
JobsSubmitted.emplace_back(sys::ExecuteNoWait(
282+
Prog, Args, /*Env=*/None, /*Redirects=*/None, /*MemoryLimit=*/0));
233283
}
234284

285+
// Wait for all commands to be executed.
286+
while (!JobsSubmitted.empty())
287+
if (int Result =
288+
checkIfJobsAreFinished(JobsSubmitted, /*BlockingWait*/ true))
289+
Res = Result;
290+
235291
if (!OutputFileList.empty()) {
236292
OS.close();
237293
}

0 commit comments

Comments
 (0)