Skip to content

Conversation

johnno1962
Copy link
Contributor

@johnno1962 johnno1962 commented Sep 10, 2025

Further to #147134 (comment), switch to use the madvise() api to page in mmap'd files and

  1. All new code compiled in #if LLVM_ENABLE_THREADS is set so it can be seen where the changes were from this PR.
  2. The new PR moves to use madvise() instead of the ad-hoc page referencing code I wrote which should avoid SIGSEGVs if the buffer is deallocated.
  3. A new property SerialBackgroundQueue().stopAllWork to be used to stop background workers when there is no further call for them. Usually the background "page-in" threads have completed first but it seems with this troublesome test this is not always the case and buffers stored in the static input file cache are being deallocated while being referenced.

@llvmbot
Copy link
Member

llvmbot commented Sep 10, 2025

@llvm/pr-subscribers-llvm-support
@llvm/pr-subscribers-llvm-binary-utilities
@llvm/pr-subscribers-lld

@llvm/pr-subscribers-lld-macho

Author: John Holdsworth (johnno1962)

Changes

Further to #147134 (comment), switch to use the madvise() api to page in mmap'd files.


Full diff: https://github.com/llvm/llvm-project/pull/157917.diff

1 Files Affected:

  • (modified) lld/MachO/Driver.cpp (+7-8)
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 3db638e1ead96..2635c82a53448 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -53,6 +53,8 @@
 #include "llvm/TextAPI/Architecture.h"
 #include "llvm/TextAPI/PackedVersion.h"
 
+#include <sys/mman.h>
+
 using namespace llvm;
 using namespace llvm::MachO;
 using namespace llvm::object;
@@ -334,11 +336,10 @@ class SerialBackgroundQueue {
 // 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;
+  std::atomic_int numDeferedFilesAdvised = 0;
   static std::atomic_uint64_t totalBytes = 0;
   auto t0 = high_resolution_clock::now();
 #endif
@@ -349,13 +350,11 @@ void multiThreadedPageInBackground(DeferredFiles &deferred) {
       return;
 #ifndef NDEBUG
     totalBytes += buff.size();
-    numDeferedFilesTouched += 1;
+    numDeferedFilesAdvised += 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;
+    // Advise that mmap'd files should be loaded into memory.
+    madvise((void *)buff.data(), buff.size(), MADV_WILLNEED);
   };
 #if LLVM_ENABLE_THREADS
   { // Create scope for waiting for the taskGroup
@@ -376,7 +375,7 @@ void multiThreadedPageInBackground(DeferredFiles &deferred) {
   auto dt = high_resolution_clock::now() - t0;
   if (Process::GetEnv("LLD_MULTI_THREAD_PAGE"))
     llvm::dbgs() << "multiThreadedPageIn " << totalBytes << "/"
-                 << numDeferedFilesTouched << "/" << deferred.size() << "/"
+                 << numDeferedFilesAdvised << "/" << deferred.size() << "/"
                  << duration_cast<milliseconds>(dt).count() / 1000. << "\n";
 #endif
 }

@johnno1962 johnno1962 changed the title Switch to use madvise() to page-in files. [lld][MachO] Follow-up to use madvise() for threaded file page-in. Sep 10, 2025
@johnno1962 johnno1962 force-pushed the threaded-advising branch 3 times, most recently from dc9ac78 to 0687bce Compare September 10, 2025 18:39
Copy link
Member

@aganea aganea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@aganea aganea requested a review from ellishg September 11, 2025 01:31
Copy link

github-actions bot commented Sep 11, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@johnno1962 johnno1962 force-pushed the threaded-advising branch 2 times, most recently from 5be953a to b87e01e Compare September 11, 2025 15:33
@johnno1962 johnno1962 force-pushed the threaded-advising branch 2 times, most recently from 1636d63 to 11711bb Compare September 11, 2025 16:00
@johnno1962
Copy link
Contributor Author

johnno1962 commented Sep 11, 2025

I've included changes to two additional files to counter the performance regression of 2 seconds from 85cd3d9 for the linker when using the --worker-threads option due to not mmap()ing many input files. Binary object and archive files don't need to be null terminated. This seemed the least intrusive change.

@johnno1962
Copy link
Contributor Author

OK, so nobody thought much of "A third alternative" so I've reverted it back to something that could be merged and is only concerned with ldd performance.

@johnno1962 johnno1962 force-pushed the threaded-advising branch 3 times, most recently from 476c34f to a8a7d65 Compare September 18, 2025 09:52
@johnno1962
Copy link
Contributor Author

johnno1962 commented Sep 18, 2025

Another more speculative commit, multi-threading the opening of files by making macho::readFile() thread safe. Another 1.5 second (10%) reduction of time spent linking the example Chrome task which originally took 25 seconds.

Thu Sep 18 11:07:02 be7af98 0m10.548s 0m10.581s 0m10.748s 0m10.485s 0m10.486s 0m10.619s 0m10.434s 0m10.682s

@johnno1962 johnno1962 force-pushed the threaded-advising branch 4 times, most recently from 5a39e60 to 94598b5 Compare September 27, 2025 10:55
Copy link
Member

@aganea aganea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a general comment for this PR, we avoid usually #ifdefs in code. I think you can write this entire PR without ever using any #ifdef LLVM_ENABLE_THREADS; either by leaving the code active at all times, or by using existing LLVM APIs that already handle that flag. An example of this is llvm::get_threadpool_strategy which falls back to not using threads when LLVM_ENABLE_THREADS is set. Similarly, ThreadPool does the same thing. Also, all places where we test for if (config->readWorkers) we could also test for llvm_is_multithreaded().

@johnno1962 johnno1962 force-pushed the threaded-advising branch 4 times, most recently from db007c8 to a5fda52 Compare September 28, 2025 16:32
@johnno1962
Copy link
Contributor Author

johnno1962 commented Sep 28, 2025

@aganea, I've removed all the #if which were intended to show clearly where the new code paths are; In practice you can tell by looking for if (config->readWorkers) anyway. I've added use of llvm_is_multithreaded() instead of another couple of #ifs . The current state of this minor PR should address the failures raised in the other thread but we won't know until it merges whether that is the case. The new SerialBackgroundWorkQueue().stopAllWork property should also avoid processing overrunning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants