diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 0e528de9c3652..e6e7194a684c1 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -164,6 +164,8 @@ static std::future createFutureForFile(std::string path) { /*RequiresNullTerminator=*/false); if (!mbOrErr) return MBErrPair{nullptr, mbOrErr.getError()}; + // Prefetch memory pages in the background as we will need them soon enough. + (*mbOrErr)->willNeedIfMmap(); return MBErrPair{std::move(*mbOrErr), std::error_code()}; }); } @@ -337,8 +339,12 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { auto retryMb = MemoryBuffer::getFile(*retryPath, /*IsText=*/false, /*RequiresNullTerminator=*/false); ec = retryMb.getError(); - if (!ec) + if (!ec) { mb = std::move(*retryMb); + // Prefetch memory pages in the background as we will need them soon + // enough. + mb->willNeedIfMmap(); + } } else { // We've already handled this file. return; diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h index cf2a8104ac813..547d732dc3053 100644 --- a/llvm/include/llvm/Support/FileSystem.h +++ b/llvm/include/llvm/Support/FileSystem.h @@ -1310,6 +1310,7 @@ class mapped_file_region { LLVM_ABI void unmapImpl(); LLVM_ABI void dontNeedImpl(); + LLVM_ABI void willNeedImpl(); LLVM_ABI std::error_code init(sys::fs::file_t FD, uint64_t Offset, mapmode Mode); @@ -1341,6 +1342,7 @@ class mapped_file_region { copyFrom(mapped_file_region()); } void dontNeed() { dontNeedImpl(); } + void willNeed() { willNeedImpl(); } LLVM_ABI size_t size() const; LLVM_ABI char *data() const; diff --git a/llvm/include/llvm/Support/MemoryBuffer.h b/llvm/include/llvm/Support/MemoryBuffer.h index f092c67265a31..9b0f1f0f7d34e 100644 --- a/llvm/include/llvm/Support/MemoryBuffer.h +++ b/llvm/include/llvm/Support/MemoryBuffer.h @@ -83,6 +83,11 @@ class LLVM_ABI MemoryBuffer { /// function should not be called on a writable buffer. virtual void dontNeedIfMmap() {} + /// Mark the buffer as to-be-used in a near future. This shall trigger OS + /// prefetching from the storage device and into memory, if possible. + /// This should be use purely as an read optimization. + virtual void willNeedIfMmap() {} + /// Open the specified file as a MemoryBuffer, returning a new MemoryBuffer /// if successful, otherwise returning null. /// diff --git a/llvm/lib/Support/MemoryBuffer.cpp b/llvm/lib/Support/MemoryBuffer.cpp index 23b9f8c5790d2..3ef4cd9086b95 100644 --- a/llvm/lib/Support/MemoryBuffer.cpp +++ b/llvm/lib/Support/MemoryBuffer.cpp @@ -243,6 +243,7 @@ class MemoryBufferMMapFile : public MB { } void dontNeedIfMmap() override { MFR.dontNeed(); } + void willNeedIfMmap() override { MFR.willNeed(); } }; } // namespace diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index 0d991ead72416..0937adcd1bc18 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -900,6 +900,19 @@ void mapped_file_region::dontNeedImpl() { #endif } +void mapped_file_region::willNeedImpl() { + assert(Mode == mapped_file_region::readonly); + if (!Mapping) + return; +#if defined(__MVS__) || defined(_AIX) + // If we don't have madvise, or it isn't beneficial, treat this as a no-op. +#elif defined(POSIX_MADV_WILLNEED) + ::posix_madvise(Mapping, Size, POSIX_MADV_WILLNEED); +#else + ::madvise(Mapping, Size, MADV_WILLNEED); +#endif +} + int mapped_file_region::alignment() { return Process::getPageSizeEstimate(); } std::error_code detail::directory_iterator_construct(detail::DirIterState &it, diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index be007b7abdb51..8be4105ba6bd8 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -1023,6 +1023,32 @@ void mapped_file_region::unmapImpl() { void mapped_file_region::dontNeedImpl() {} +void mapped_file_region::willNeedImpl() { +#if (_WIN32_WINNT < _WIN32_WINNT_WIN8) + typedef struct _WIN32_MEMORY_RANGE_ENTRY { + PVOID VirtualAddress; + SIZE_T NumberOfBytes; + } WIN32_MEMORY_RANGE_ENTRY, *PWIN32_MEMORY_RANGE_ENTRY; +#endif + + HMODULE kernelM = llvm::sys::windows::loadSystemModuleSecure(L"kernel32.dll"); + if (kernelM) { + // PrefetchVirtualMemory is only available on Windows 8 and later. Since we + // still support compilation on Windows 7, we load the function dynamically. + typedef BOOL(WINAPI * PrefetchVirtualMemory_t)( + HANDLE hProcess, ULONG_PTR NumberOfEntries, + _In_reads_(NumberOfEntries) PWIN32_MEMORY_RANGE_ENTRY VirtualAddresses, + ULONG Flags); + static const auto pfnPrefetchVirtualMemory = + (PrefetchVirtualMemory_t)::GetProcAddress(kernelM, + "PrefetchVirtualMemory"); + if (pfnPrefetchVirtualMemory) { + WIN32_MEMORY_RANGE_ENTRY Range{Mapping, Size}; + pfnPrefetchVirtualMemory(::GetCurrentProcess(), 1, &Range, 0); + } + } +} + std::error_code mapped_file_region::sync() const { if (!::FlushViewOfFile(Mapping, Size)) return mapWindowsError(GetLastError());