Read the PersistentMap's backing file into memory all at once when processing it. We are going to read the whole thing and convert it into data structures, anyway.
This shaves some time off of server startup time.
--
PiperOrigin-RevId: 148377493
MOS_MIGRATED_REVID=148377493
diff --git a/src/main/java/com/google/devtools/build/lib/util/PersistentMap.java b/src/main/java/com/google/devtools/build/lib/util/PersistentMap.java
index 56ac9a1..4a73486 100644
--- a/src/main/java/com/google/devtools/build/lib/util/PersistentMap.java
+++ b/src/main/java/com/google/devtools/build/lib/util/PersistentMap.java
@@ -15,18 +15,22 @@
package com.google.devtools.build.lib.util;
import com.google.common.collect.ForwardingMap;
+import com.google.common.io.ByteStreams;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.logging.Logger;
/**
* A map that is backed by persistent storage. It uses two files on disk for
@@ -65,8 +69,10 @@
public abstract class PersistentMap<K, V> extends ForwardingMap<K, V> {
private static final int MAGIC = 0x20071105;
-
private static final int ENTRY_MAGIC = 0xfe;
+ private static final int MIN_MAPFILE_SIZE = 16;
+ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+ private static final Logger LOG = Logger.getLogger(PersistentMap.class.getName());
private final int version;
private final Path mapFile;
@@ -334,16 +340,30 @@
if (!mapFile.exists()) {
return;
}
- DataInputStream in = new DataInputStream(new BufferedInputStream(mapFile.getInputStream()));
- try {
- long fileSize = mapFile.getFileSize();
- if (fileSize < (16)) {
- if (failFast) {
- throw new IOException(mapFile + " is too short: Only " + fileSize + " bytes");
- } else {
- return;
- }
+
+ long fileSize = mapFile.getFileSize();
+ if (fileSize < MIN_MAPFILE_SIZE) {
+ if (failFast) {
+ throw new IOException(mapFile + " is too short: Only " + fileSize + " bytes");
+ } else {
+ return;
}
+ } else if (fileSize > MAX_ARRAY_SIZE) {
+ if (failFast) {
+ throw new IOException(mapFile + " is too long: " + fileSize + " bytes");
+ } else {
+ return;
+ }
+ }
+
+ // We read the whole file up front as a performance optimization; otherwise calling available()
+ // on the stream over and over does a lot of syscalls.
+ byte[] mapBytes;
+ try (InputStream fileInput = mapFile.getInputStream()) {
+ mapBytes = ByteStreams.toByteArray(new BufferedInputStream(fileInput));
+ }
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(mapBytes));
+ try {
if (in.readLong() != MAGIC) { // not a PersistentMap
if (failFast) {
throw new IOException("Unexpected format");
@@ -360,6 +380,8 @@
} finally {
in.close();
}
+
+ LOG.info(String.format("Loaded cache '%s' [%s bytes]", mapFile, fileSize));
}
/**