Skip to content

Commit 5a13fac

Browse files
committed
[libc] Add loader option to force serial execution of GPU region
Summary: The loader is used as a test utility to run traditionally CPU based unit tests on the GPU. This has issues when used with something like `llvm-lit` because the GPU runtimes have a nasty habit of either running out of resources or hanging when they are overloaded. To combat this, I added this option to force each process to perform the GPU part serially. This is done right now with a simple file lock on the executing file. I was originally thinking about using more complex IPC to allow N processes to share execution, but that seemed overly complicated given the incredibly large number of failure modes it introduces. File locks are nice here because if the process crashes or is killed it will release the lock automatically (at least on Linux). This is in contrast to something like POSIX shared memory which will stick around until it's unlinked, meaning that if someone did `sigkill` on the program it would never get cleaned up and other threads might wait on a mutex that never occurs. Restricting this to one thread isn't overly ideal, given the fact that the runtime can likely handle at least a *few* separate processes, but this was easy and it works, so might as well start here. This will hopefully unblock me on running `libcxx` tests, as those ran with so much parallelism spurious failures were very common.
1 parent 97f723b commit 5a13fac

File tree

1 file changed

+33
-0
lines changed

1 file changed

+33
-0
lines changed

libc/utils/gpu/loader/Main.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@
1616
#include "llvm/BinaryFormat/Magic.h"
1717
#include "llvm/Support/CommandLine.h"
1818
#include "llvm/Support/Error.h"
19+
#include "llvm/Support/FileSystem.h"
1920
#include "llvm/Support/MemoryBuffer.h"
21+
#include "llvm/Support/Path.h"
2022
#include "llvm/Support/Signals.h"
2123
#include "llvm/Support/WithColor.h"
2224

25+
#include <cerrno>
2326
#include <cstdio>
2427
#include <cstdlib>
28+
#include <cstring>
2529
#include <string>
30+
#include <sys/file.h>
2631

2732
using namespace llvm;
2833

@@ -62,6 +67,12 @@ static cl::opt<bool>
6267
cl::desc("Output resource usage of launched kernels"),
6368
cl::init(false), cl::cat(loader_category));
6469

70+
static cl::opt<bool>
71+
no_parallelism("no-parallelism",
72+
cl::desc("Allows only a single process to use the GPU at a "
73+
"time. Useful to suppress out-of-resource errors"),
74+
cl::init(false), cl::cat(loader_category));
75+
6576
static cl::opt<std::string> file(cl::Positional, cl::Required,
6677
cl::desc("<gpu executable>"),
6778
cl::cat(loader_category));
@@ -75,6 +86,12 @@ static cl::list<std::string> args(cl::ConsumeAfter,
7586
exit(EXIT_FAILURE);
7687
}
7788

89+
std::string get_main_executable(const char *name) {
90+
void *ptr = (void *)(intptr_t)&get_main_executable;
91+
auto cow_path = sys::fs::getMainExecutable(name, ptr);
92+
return sys::path::parent_path(cow_path).str();
93+
}
94+
7895
int main(int argc, const char **argv, const char **envp) {
7996
sys::PrintStackTraceOnErrorSignal(argv[0]);
8097
cl::HideUnrelatedOptions(loader_category);
@@ -98,12 +115,28 @@ int main(int argc, const char **argv, const char **envp) {
98115
llvm::transform(args, std::back_inserter(new_argv),
99116
[](const std::string &arg) { return arg.c_str(); });
100117

118+
// Claim a file lock on the executable so only a single process can enter this
119+
// region if requested. This prevents the loader from spurious failures.
120+
int fd = -1;
121+
if (no_parallelism) {
122+
fd = open(get_main_executable(argv[0]).c_str(), O_RDONLY);
123+
if (flock(fd, LOCK_EX) == -1)
124+
report_error(createStringError("Failed to lock '%s': %s", argv[0],
125+
strerror(errno)));
126+
}
127+
101128
// Drop the loader from the program arguments.
102129
LaunchParameters params{threads_x, threads_y, threads_z,
103130
blocks_x, blocks_y, blocks_z};
104131
int ret = load(new_argv.size(), new_argv.data(), envp,
105132
const_cast<char *>(image.getBufferStart()),
106133
image.getBufferSize(), params, print_resource_usage);
107134

135+
if (no_parallelism) {
136+
if (flock(fd, LOCK_UN) == -1)
137+
report_error(createStringError("Failed to unlock '%s': %s", argv[0],
138+
strerror(errno)));
139+
}
140+
108141
return ret;
109142
}

0 commit comments

Comments
 (0)