Skip to content

Commit c55b5b2

Browse files
committed
Multi-threaded disk i/o.
1 parent e2510b1 commit c55b5b2

File tree

3 files changed

+106
-9
lines changed

3 files changed

+106
-9
lines changed

lld/MachO/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ struct Configuration {
186186
bool interposable = false;
187187
bool errorForArchMismatch = false;
188188
bool ignoreAutoLink = false;
189+
int readThreads = 0;
189190
// ld64 allows invalid auto link options as long as the link succeeds. LLD
190191
// does not, but there are cases in the wild where the invalid linker options
191192
// exist. This allows users to ignore the specific invalid options in the case

lld/MachO/Driver.cpp

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "llvm/Support/FileSystem.h"
4545
#include "llvm/Support/Parallel.h"
4646
#include "llvm/Support/Path.h"
47+
#include "llvm/Support/Process.h"
4748
#include "llvm/Support/TarWriter.h"
4849
#include "llvm/Support/TargetSelect.h"
4950
#include "llvm/Support/TimeProfiler.h"
@@ -282,11 +283,11 @@ static void saveThinArchiveToRepro(ArchiveFile const *file) {
282283
": Archive::children failed: " + toString(std::move(e)));
283284
}
284285

285-
static InputFile *addFile(StringRef path, LoadType loadType,
286-
bool isLazy = false, bool isExplicit = true,
287-
bool isBundleLoader = false,
288-
bool isForceHidden = false) {
289-
std::optional<MemoryBufferRef> buffer = readFile(path);
286+
static InputFile *deferredAddFile(std::optional<MemoryBufferRef> buffer,
287+
StringRef path, LoadType loadType,
288+
bool isLazy = false, bool isExplicit = true,
289+
bool isBundleLoader = false,
290+
bool isForceHidden = false) {
290291
if (!buffer)
291292
return nullptr;
292293
MemoryBufferRef mbref = *buffer;
@@ -441,6 +442,14 @@ static InputFile *addFile(StringRef path, LoadType loadType,
441442
return newFile;
442443
}
443444

445+
static InputFile *addFile(StringRef path, LoadType loadType,
446+
bool isLazy = false, bool isExplicit = true,
447+
bool isBundleLoader = false,
448+
bool isForceHidden = false) {
449+
return deferredAddFile(readFile(path), path, loadType, isLazy, isExplicit,
450+
isBundleLoader, isForceHidden);
451+
}
452+
444453
static std::vector<StringRef> missingAutolinkWarnings;
445454
static void addLibrary(StringRef name, bool isNeeded, bool isWeak,
446455
bool isReexport, bool isHidden, bool isExplicit,
@@ -564,13 +573,23 @@ void macho::resolveLCLinkerOptions() {
564573
}
565574
}
566575

567-
static void addFileList(StringRef path, bool isLazy) {
576+
typedef struct {
577+
StringRef path;
578+
std::optional<MemoryBufferRef> buffer;
579+
} DeferredFile;
580+
581+
static void addFileList(StringRef path, bool isLazy,
582+
std::vector<DeferredFile> &deferredFiles) {
568583
std::optional<MemoryBufferRef> buffer = readFile(path);
569584
if (!buffer)
570585
return;
571586
MemoryBufferRef mbref = *buffer;
572587
for (StringRef path : args::getLines(mbref))
573-
addFile(rerootPath(path), LoadType::CommandLine, isLazy);
588+
if (config->readThreads) {
589+
StringRef rrpath = rerootPath(path);
590+
deferredFiles.push_back({rrpath, readFile(rrpath)});
591+
} else
592+
addFile(rerootPath(path), LoadType::CommandLine, isLazy);
574593
}
575594

576595
// We expect sub-library names of the form "libfoo", which will match a dylib
@@ -1215,20 +1234,80 @@ static void handleSymbolPatterns(InputArgList &args,
12151234
parseSymbolPatternsFile(arg, symbolPatterns);
12161235
}
12171236

1218-
static void createFiles(const InputArgList &args) {
1237+
// Most input files have been mapped but not yet paged in.
1238+
// This code forces the page-ins on multiple threads so
1239+
// the process is not stalled waiting on disk buffer i/o.
1240+
void multiThreadedPageIn(std::vector<DeferredFile> &deferred, int nthreads) {
1241+
#ifndef _WIN32
1242+
typedef struct {
1243+
std::vector<DeferredFile> &deferred;
1244+
size_t counter, total, pageSize;
1245+
pthread_mutex_t mutex;
1246+
} PageInState;
1247+
PageInState state = {deferred, 0, 0,
1248+
llvm::sys::Process::getPageSizeEstimate(),
1249+
pthread_mutex_t()};
1250+
pthread_mutex_init(&state.mutex, NULL);
1251+
1252+
pthread_t running[200];
1253+
int maxthreads = sizeof running / sizeof running[0];
1254+
if (nthreads > maxthreads)
1255+
nthreads = maxthreads;
1256+
1257+
for (int t = 0; t < nthreads; t++)
1258+
pthread_create(
1259+
&running[t], nullptr,
1260+
[](void *ptr) -> void * {
1261+
PageInState &state = *(PageInState *)ptr;
1262+
static int total = 0;
1263+
while (true) {
1264+
pthread_mutex_lock(&state.mutex);
1265+
if (state.counter >= state.deferred.size()) {
1266+
pthread_mutex_unlock(&state.mutex);
1267+
return nullptr;
1268+
}
1269+
DeferredFile &add = state.deferred[state.counter];
1270+
state.counter += 1;
1271+
pthread_mutex_unlock(&state.mutex);
1272+
1273+
int t = 0; // Reference each page to load it into memory.
1274+
for (const char *page = add.buffer->getBuffer().data(),
1275+
*end = page + add.buffer->getBuffer().size();
1276+
page < end; page += state.pageSize)
1277+
t += *page;
1278+
state.total += t; // Avoids whole section being optimised out.
1279+
}
1280+
},
1281+
&state);
1282+
1283+
for (int t = 0; t < nthreads; t++)
1284+
pthread_join(running[t], nullptr);
1285+
1286+
pthread_mutex_destroy(&state.mutex);
1287+
#endif
1288+
}
1289+
1290+
void createFiles(const InputArgList &args) {
12191291
TimeTraceScope timeScope("Load input files");
12201292
// This loop should be reserved for options whose exact ordering matters.
12211293
// Other options should be handled via filtered() and/or getLastArg().
12221294
bool isLazy = false;
12231295
// If we've processed an opening --start-lib, without a matching --end-lib
12241296
bool inLib = false;
1297+
std::vector<DeferredFile> deferredFiles;
1298+
12251299
for (const Arg *arg : args) {
12261300
const Option &opt = arg->getOption();
12271301
warnIfDeprecatedOption(opt);
12281302
warnIfUnimplementedOption(opt);
12291303

12301304
switch (opt.getID()) {
12311305
case OPT_INPUT:
1306+
if (config->readThreads) {
1307+
StringRef rrpath = rerootPath(arg->getValue());
1308+
deferredFiles.push_back({rrpath, readFile(rrpath)});
1309+
break;
1310+
}
12321311
addFile(rerootPath(arg->getValue()), LoadType::CommandLine, isLazy);
12331312
break;
12341313
case OPT_needed_library:
@@ -1249,7 +1328,7 @@ static void createFiles(const InputArgList &args) {
12491328
dylibFile->forceWeakImport = true;
12501329
break;
12511330
case OPT_filelist:
1252-
addFileList(arg->getValue(), isLazy);
1331+
addFileList(arg->getValue(), isLazy, deferredFiles);
12531332
break;
12541333
case OPT_force_load:
12551334
addFile(rerootPath(arg->getValue()), LoadType::CommandLineForce);
@@ -1295,6 +1374,12 @@ static void createFiles(const InputArgList &args) {
12951374
break;
12961375
}
12971376
}
1377+
1378+
if (config->readThreads) {
1379+
multiThreadedPageIn(deferredFiles, config->readThreads);
1380+
for (auto &add : deferredFiles)
1381+
deferredAddFile(add.buffer, add.path, LoadType::CommandLine, isLazy);
1382+
}
12981383
}
12991384

13001385
static void gatherInputSections() {
@@ -1687,6 +1772,14 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
16871772
}
16881773
}
16891774

1775+
if (auto *arg = args.getLastArg(OPT_read_threads)) {
1776+
StringRef v(arg->getValue());
1777+
unsigned threads = 0;
1778+
if (!llvm::to_integer(v, threads, 0) || threads < 0)
1779+
error(arg->getSpelling() + ": expected a positive integer, but got '" +
1780+
arg->getValue() + "'");
1781+
config->readThreads = threads;
1782+
}
16901783
if (auto *arg = args.getLastArg(OPT_threads_eq)) {
16911784
StringRef v(arg->getValue());
16921785
unsigned threads = 0;

lld/MachO/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ def dead_strip : Flag<["-"], "dead_strip">,
396396
def interposable : Flag<["-"], "interposable">,
397397
HelpText<"Indirects access to all exported symbols in an image">,
398398
Group<grp_opts>;
399+
def read_threads : Joined<["--"], "read-threads=">,
400+
HelpText<"Number of threads to use paging in files.">,
401+
Group<grp_lld>;
399402
def order_file : Separate<["-"], "order_file">,
400403
MetaVarName<"<file>">,
401404
HelpText<"Layout functions and data according to specification in <file>">,

0 commit comments

Comments
 (0)