Performance optimization for isSymbolicLink() calls on Windows
Current implementation for WindowsFileSystem.isSymbolicLink() makes unnecessary system calls which significantly affect the performance. Original code goes through the following:
- AbstractFileSystemWithCustomStat.isSymbolicLink
- WindowsFileSystem.stat
- WindowsFileSystem.getIoFile
- JavaIoFileSystem.getNioPath
- Files.readAttributes
- WindowsFileSystem.fileIsSymbolicLink
- WindowsFileOperations.getLastChangeTime
This implementation skips most of them.
Closes #24047.
PiperOrigin-RevId: 690964117
Change-Id: I2b407d9c69af62e770684d868d04e60d7ce1773e
diff --git a/src/main/java/com/google/devtools/build/lib/windows/BUILD b/src/main/java/com/google/devtools/build/lib/windows/BUILD
index 20809bb..8294631 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/windows/BUILD
@@ -42,5 +42,6 @@
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
"//third_party:guava",
+ "//third_party:jsr305",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
index 9c35178..78b4c23 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
@@ -27,6 +27,7 @@
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.attribute.DosFileAttributes;
+import javax.annotation.Nullable;
/** File system implementation for Windows. */
@ThreadSafe
@@ -148,14 +149,14 @@
throw new FileNotFoundException(path + ERR_NO_SUCH_FILE_OR_DIR);
}
- final boolean isSymbolicLink = !followSymlinks && fileIsSymbolicLink(file);
- final long lastChangeTime =
- WindowsFileOperations.getLastChangeTime(getNioPath(path).toString(), followSymlinks);
FileStatus status =
new FileStatus() {
+ @Nullable volatile Boolean isSymbolicLink; // null if not yet known
+ volatile long lastChangeTime = -1;
+
@Override
public boolean isFile() {
- return !isSymbolicLink && (attributes.isRegularFile() || isSpecialFile());
+ return !isSymbolicLink() && (attributes.isRegularFile() || isSpecialFile());
}
@Override
@@ -163,16 +164,19 @@
// attributes.isOther() returns false for symlinks but returns true for junctions.
// Bazel treats junctions like symlinks. So let's return false here for junctions.
// This fixes https://github.com/bazelbuild/bazel/issues/9176
- return !isSymbolicLink && attributes.isOther();
+ return !isSymbolicLink() && attributes.isOther();
}
@Override
public boolean isDirectory() {
- return !isSymbolicLink && attributes.isDirectory();
+ return !isSymbolicLink() && attributes.isDirectory();
}
@Override
public boolean isSymbolicLink() {
+ if (isSymbolicLink == null) {
+ isSymbolicLink = !followSymlinks && fileIsSymbolicLink(file);
+ }
return isSymbolicLink;
}
@@ -187,7 +191,12 @@
}
@Override
- public long getLastChangeTime() {
+ public long getLastChangeTime() throws IOException {
+ if (lastChangeTime == -1) {
+ lastChangeTime =
+ WindowsFileOperations.getLastChangeTime(
+ getNioPath(path).toString(), followSymlinks);
+ }
return lastChangeTime;
}
@@ -208,6 +217,11 @@
}
@Override
+ protected boolean isSymbolicLink(PathFragment path) {
+ return fileIsSymbolicLink(getIoFile(path));
+ }
+
+ @Override
protected boolean isDirectory(PathFragment path, boolean followSymlinks) {
if (!followSymlinks) {
try {