Skip to content

Commit 6d12eae

Browse files
committed
Remove existing file in a separate thread asynchronously.
On Linux (and probably on other Unix-like systems), unlink(2) is noticeably slow. It takes 250 milliseconds to remove a 1 GB file on ext4 filesystem on my machine, whether the file is on SSD or on a spinning disk. To create a new result file, we remove existing file first. So, if you repeatedly link a 1 GB program in a regular compile-link-debug cycle, every cycle wastes 250 milliseconds only to remove a file. Since LLD can link a 1 GB in about 5 seconds, that waste actually matters. This patch defines `unlinkAsync` function. The function spawns a background thread to call unlink. The calling thread returns almost immediately. Differential Revision: https://reviews.llvm.org/D27295 llvm-svn: 288680
1 parent 28f05ac commit 6d12eae

File tree

1 file changed

+38
-0
lines changed

1 file changed

+38
-0
lines changed

lld/ELF/Writer.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
#include "llvm/ADT/StringMap.h"
2222
#include "llvm/ADT/StringSwitch.h"
2323
#include "llvm/Support/FileOutputBuffer.h"
24+
#include "llvm/Support/FileSystem.h"
2425
#include "llvm/Support/raw_ostream.h"
2526
#include <climits>
27+
#include <thread>
2628

2729
using namespace llvm;
2830
using namespace llvm::ELF;
@@ -1504,10 +1506,46 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
15041506
Sec->writeHeaderTo<ELFT>(++SHdrs);
15051507
}
15061508

1509+
// Removes a given file asynchronously. This is a performance hack,
1510+
// so remove this when operating systems are improved.
1511+
//
1512+
// On Linux (and probably on other Unix-like systems), unlink(2) is a
1513+
// noticeably slow system call. As of 2016, unlink takes 250
1514+
// milliseconds to remove a 1 GB file on ext4 filesystem on my machine.
1515+
//
1516+
// To create a new result file, we first remove existing file. So, if
1517+
// you repeatedly link a 1 GB program in a regular compile-link-debug
1518+
// cycle, every cycle wastes 250 milliseconds only to remove a file.
1519+
// Since LLD can link a 1 GB binary in about 5 seconds, that waste
1520+
// actually counts.
1521+
//
1522+
// This function spawns a background thread to call unlink.
1523+
// The calling thread returns almost immediately.
1524+
static void unlinkAsync(StringRef Path) {
1525+
if (!Config->Threads || !sys::fs::exists(Config->OutputFile))
1526+
return;
1527+
1528+
// First, rename Path to avoid race condition. We cannot remomve
1529+
// Path from a different thread because we are now going to create
1530+
// Path as a new file. If we do that in a different thread, the new
1531+
// thread can remove the new file.
1532+
SmallString<128> TempPath;
1533+
if (auto EC = sys::fs::createUniqueFile(Path + "tmp%%%%%%%%", TempPath))
1534+
fatal(EC, "createUniqueFile failed");
1535+
if (auto EC = sys::fs::rename(Path, TempPath))
1536+
fatal(EC, "rename failed");
1537+
1538+
// Remove TempPath in background.
1539+
std::thread([=] { ::remove(TempPath.str().str().c_str()); }).detach();
1540+
}
1541+
1542+
// Open a result file.
15071543
template <class ELFT> void Writer<ELFT>::openFile() {
1544+
unlinkAsync(Config->OutputFile);
15081545
ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
15091546
FileOutputBuffer::create(Config->OutputFile, FileSize,
15101547
FileOutputBuffer::F_executable);
1548+
15111549
if (auto EC = BufferOrErr.getError())
15121550
error(EC, "failed to open " + Config->OutputFile);
15131551
else

0 commit comments

Comments
 (0)