[lld] [lld][MachO]Multi-threaded i/o. Twice as fast linking a large project. (PR #147134)
John Holdsworth via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 23 04:42:47 PDT 2025
================
@@ -282,11 +284,117 @@ static void saveThinArchiveToRepro(ArchiveFile const *file) {
": Archive::children failed: " + toString(std::move(e)));
}
-static InputFile *addFile(StringRef path, LoadType loadType,
- bool isLazy = false, bool isExplicit = true,
- bool isBundleLoader = false,
- bool isForceHidden = false) {
- std::optional<MemoryBufferRef> buffer = readFile(path);
+struct DeferredFile {
+ StringRef path;
+ bool isLazy;
+ MemoryBufferRef buffer;
+};
+using DeferredFiles = std::vector<DeferredFile>;
+
+class SerialBackgroundQueue {
+ std::deque<std::function<void()>> queue;
+ std::thread *running;
+ std::mutex mutex;
+
+public:
+ void queueWork(std::function<void()> work) {
+ mutex.lock();
+ if (running && queue.empty()) {
+ mutex.unlock();
+ running->join();
+ mutex.lock();
+ delete running;
+ running = nullptr;
+ }
+
+ if (work) {
+ queue.emplace_back(std::move(work));
+ if (!running)
+ running = new std::thread([&]() {
+ while (true) {
+ mutex.lock();
+ if (queue.empty()) {
+ mutex.unlock();
+ break;
+ }
+ auto work = std::move(queue.front());
+ mutex.unlock();
+ work();
+ mutex.lock();
+ queue.pop_front();
+ mutex.unlock();
+ }
+ });
+ }
+ mutex.unlock();
+ }
+};
+
+// Most input files have been mapped but not yet paged in.
+// This code forces the page-ins on multiple threads so
+// the process is not stalled waiting on disk buffer i/o.
+void multiThreadedPageInBackground(DeferredFiles &deferred) {
+ static const size_t pageSize = Process::getPageSizeEstimate();
+ static const size_t largeArchive = 10 * 1024 * 1024;
+#ifndef NDEBUG
+ using namespace std::chrono;
+ std::atomic_int numDeferedFilesTouched = 0;
+ static std::atomic_uint64_t totalBytes = 0;
+ auto t0 = high_resolution_clock::now();
+#endif
+
+ auto preloadDeferredFile = [&](const DeferredFile &deferredFile) {
+ const StringRef &buff = deferredFile.buffer.getBuffer();
+ if (buff.size() > largeArchive)
+ return;
+#ifndef NDEBUG
+ totalBytes += buff.size();
+ numDeferedFilesTouched += 1;
+#endif
+
+ // Reference all file's mmap'd pages to load them into memory.
+ for (const char *page = buff.data(), *end = page + buff.size(); page < end;
+ page += pageSize)
+ LLVM_ATTRIBUTE_UNUSED volatile char t = *page;
----------------
johnno1962 wrote:
Hi, I guess if the paging thread is held up for any reason it could still be running when global deallocations take place; The test that failed is a bit of an edge case with a single file with almost no processing. I think the best way to avoid this would be to move ahead with the conversion of the code to use madvise() that was being explored in. the [other PR](https://github.com/llvm/llvm-project/pull/157917). I've rolled back the latest commit and we are back to where we were on the 12th, ready to merge before I started experimenting and everyone lost interest.
https://github.com/llvm/llvm-project/pull/147134
More information about the llvm-commits
mailing list