]> granicus.if.org Git - llvm/commitdiff
Add a ThinLTO cache policy for controlling the maximum cache size in bytes.
authorPeter Collingbourne <peter@pcc.me.uk>
Fri, 23 Jun 2017 17:05:03 +0000 (17:05 +0000)
committerPeter Collingbourne <peter@pcc.me.uk>
Fri, 23 Jun 2017 17:05:03 +0000 (17:05 +0000)
This is useful when an upper limit on the cache size needs to be
controlled independently of the amount of the amount of free space.

One use case is a machine with a large number of cache directories
(e.g. a buildbot slave hosting a large number of independent build
jobs). By imposing an upper size limit on each cache directory,
users can more easily estimate the server's capacity.

Differential Revision: https://reviews.llvm.org/D34547

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@306126 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/LTO/legacy/ThinLTOCodeGenerator.h
include/llvm/Support/CachePruning.h
lib/Support/CachePruning.cpp
unittests/Support/CachePruningTest.cpp

index f9545333aabdf187d68426fb2117307cc49cf93e..14f0c48266f0b7c19ec9a391ce06b6b72afd1dec 100644 (file)
@@ -177,7 +177,7 @@ public:
    */
   void setMaxCacheSizeRelativeToAvailableSpace(unsigned Percentage) {
     if (Percentage)
-      CacheOptions.Policy.PercentageOfAvailableSpace = Percentage;
+      CacheOptions.Policy.MaxSizePercentageOfAvailableSpace = Percentage;
   }
 
   /**@}*/
index e826938878e50e3e9a59185aa9ff4035028e6fe0..46e34358573bf06004927ef5cd3c7b3f6671056f 100644 (file)
@@ -39,8 +39,13 @@ struct CachePruningPolicy {
   /// available space on the the disk. Set to 100 to indicate no limit, 50 to
   /// indicate that the cache size will not be left over half the available disk
   /// space. A value over 100 will be reduced to 100. A value of 0 disables the
-  /// size-based pruning.
-  unsigned PercentageOfAvailableSpace = 75;
+  /// percentage size-based pruning.
+  unsigned MaxSizePercentageOfAvailableSpace = 75;
+
+  /// The maximum size for the cache directory in bytes. A value over the amount
+  /// of available space on the disk will be reduced to the amount of available
+  /// space. A value of 0 disables the absolute size-based pruning.
+  uint64_t MaxSizeBytes = 0;
 };
 
 /// Parse the given string as a cache pruning policy. Defaults are taken from a
index aca1236395655839ca60fca32252113e4139e344..303ad219746806168dbb41b6f72a9529e36d36fe 100644 (file)
@@ -79,10 +79,10 @@ llvm::parseCachePruningPolicy(StringRef PolicyStr) {
         return DurationOrErr.takeError();
       Policy.Expiration = *DurationOrErr;
     } else if (Key == "cache_size") {
-      if (Value.back() != '%')
-        return make_error<StringError>("'" + Value + "' must be a percentage",
-                                       inconvertibleErrorCode());
-      StringRef SizeStr = Value.slice(0, Value.size() - 1);
+    if (Value.back() != '%')
+      return make_error<StringError>("'" + Value + "' must be a percentage",
+                                     inconvertibleErrorCode());
+      StringRef SizeStr = Value.drop_back();
       uint64_t Size;
       if (SizeStr.getAsInteger(0, Size))
         return make_error<StringError>("'" + SizeStr + "' not an integer",
@@ -91,7 +91,28 @@ llvm::parseCachePruningPolicy(StringRef PolicyStr) {
         return make_error<StringError>("'" + SizeStr +
                                            "' must be between 0 and 100",
                                        inconvertibleErrorCode());
-      Policy.PercentageOfAvailableSpace = Size;
+      Policy.MaxSizePercentageOfAvailableSpace = Size;
+    } else if (Key == "cache_size_bytes") {
+      uint64_t Mult = 1;
+      switch (Value.back()) {
+      case 'k':
+        Mult = 1024;
+        Value = Value.drop_back();
+        break;
+      case 'm':
+        Mult = 1024 * 1024;
+        Value = Value.drop_back();
+        break;
+      case 'g':
+        Mult = 1024 * 1024 * 1024;
+        Value = Value.drop_back();
+        break;
+      }
+      uint64_t Size;
+      if (Value.getAsInteger(0, Size))
+        return make_error<StringError>("'" + Value + "' not an integer",
+                                       inconvertibleErrorCode());
+      Policy.MaxSizeBytes = Size * Mult;
     } else {
       return make_error<StringError>("Unknown key: '" + Key + "'",
                                      inconvertibleErrorCode());
@@ -115,11 +136,12 @@ bool llvm::pruneCache(StringRef Path, CachePruningPolicy Policy) {
   if (!isPathDir)
     return false;
 
-  Policy.PercentageOfAvailableSpace =
-      std::min(Policy.PercentageOfAvailableSpace, 100u);
+  Policy.MaxSizePercentageOfAvailableSpace =
+      std::min(Policy.MaxSizePercentageOfAvailableSpace, 100u);
 
   if (Policy.Expiration == seconds(0) &&
-      Policy.PercentageOfAvailableSpace == 0) {
+      Policy.MaxSizePercentageOfAvailableSpace == 0 &&
+      Policy.MaxSizeBytes == 0) {
     DEBUG(dbgs() << "No pruning settings set, exit early\n");
     // Nothing will be pruned, early exit
     return false;
@@ -157,7 +179,8 @@ bool llvm::pruneCache(StringRef Path, CachePruningPolicy Policy) {
     writeTimestampFile(TimestampFile);
   }
 
-  bool ShouldComputeSize = (Policy.PercentageOfAvailableSpace > 0);
+  bool ShouldComputeSize =
+      (Policy.MaxSizePercentageOfAvailableSpace > 0 || Policy.MaxSizeBytes > 0);
 
   // Keep track of space
   std::set<std::pair<uint64_t, std::string>> FileSizes;
@@ -216,14 +239,22 @@ bool llvm::pruneCache(StringRef Path, CachePruningPolicy Policy) {
     }
     sys::fs::space_info SpaceInfo = ErrOrSpaceInfo.get();
     auto AvailableSpace = TotalSize + SpaceInfo.free;
-    auto FileAndSize = FileSizes.rbegin();
+
+    if (Policy.MaxSizePercentageOfAvailableSpace == 0)
+      Policy.MaxSizePercentageOfAvailableSpace = 100;
+    if (Policy.MaxSizeBytes == 0)
+      Policy.MaxSizeBytes = AvailableSpace;
+    auto TotalSizeTarget = std::min<uint64_t>(
+        AvailableSpace * Policy.MaxSizePercentageOfAvailableSpace / 100ull,
+        Policy.MaxSizeBytes);
+
     DEBUG(dbgs() << "Occupancy: " << ((100 * TotalSize) / AvailableSpace)
-                 << "% target is: " << Policy.PercentageOfAvailableSpace
-                 << "\n");
+                 << "% target is: " << Policy.MaxSizePercentageOfAvailableSpace
+                 << "%, " << Policy.MaxSizeBytes << " bytes\n");
+
+    auto FileAndSize = FileSizes.rbegin();
     // Remove the oldest accessed files first, till we get below the threshold
-    while (((100 * TotalSize) / AvailableSpace) >
-               Policy.PercentageOfAvailableSpace &&
-           FileAndSize != FileSizes.rend()) {
+    while (TotalSize > TotalSizeTarget && FileAndSize != FileSizes.rend()) {
       // Remove the file.
       sys::fs::remove(FileAndSize->second);
       // Update size
index 04ac0d09b49356ae5d9a3878b6f9eae05eac76bd..97d554eabc32ae3a2954fdbadee67d97281958c2 100644 (file)
@@ -18,7 +18,7 @@ TEST(CachePruningPolicyParser, Empty) {
   ASSERT_TRUE(bool(P));
   EXPECT_EQ(std::chrono::seconds(1200), P->Interval);
   EXPECT_EQ(std::chrono::hours(7 * 24), P->Expiration);
-  EXPECT_EQ(75u, P->PercentageOfAvailableSpace);
+  EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);
 }
 
 TEST(CachePruningPolicyParser, Interval) {
@@ -39,10 +39,30 @@ TEST(CachePruningPolicyParser, Expiration) {
   EXPECT_EQ(std::chrono::seconds(1), P->Expiration);
 }
 
-TEST(CachePruningPolicyParser, PercentageOfAvailableSpace) {
+TEST(CachePruningPolicyParser, MaxSizePercentageOfAvailableSpace) {
   auto P = parseCachePruningPolicy("cache_size=100%");
   ASSERT_TRUE(bool(P));
-  EXPECT_EQ(100u, P->PercentageOfAvailableSpace);
+  EXPECT_EQ(100u, P->MaxSizePercentageOfAvailableSpace);
+  EXPECT_EQ(0u, P->MaxSizeBytes);
+}
+
+TEST(CachePruningPolicyParser, MaxSizeBytes) {
+  auto P = parseCachePruningPolicy("cache_size_bytes=1");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);
+  EXPECT_EQ(1u, P->MaxSizeBytes);
+  P = parseCachePruningPolicy("cache_size_bytes=2k");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);
+  EXPECT_EQ(2u * 1024u, P->MaxSizeBytes);
+  P = parseCachePruningPolicy("cache_size_bytes=3m");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);
+  EXPECT_EQ(3u * 1024u * 1024u, P->MaxSizeBytes);
+  P = parseCachePruningPolicy("cache_size_bytes=4g");
+  ASSERT_TRUE(bool(P));
+  EXPECT_EQ(75u, P->MaxSizePercentageOfAvailableSpace);
+  EXPECT_EQ(4ull * 1024ull * 1024ull * 1024ull, P->MaxSizeBytes);
 }
 
 TEST(CachePruningPolicyParser, Multiple) {
@@ -50,7 +70,7 @@ TEST(CachePruningPolicyParser, Multiple) {
   ASSERT_TRUE(bool(P));
   EXPECT_EQ(std::chrono::seconds(1200), P->Interval);
   EXPECT_EQ(std::chrono::seconds(1), P->Expiration);
-  EXPECT_EQ(50u, P->PercentageOfAvailableSpace);
+  EXPECT_EQ(50u, P->MaxSizePercentageOfAvailableSpace);
 }
 
 TEST(CachePruningPolicyParser, Errors) {
@@ -66,6 +86,12 @@ TEST(CachePruningPolicyParser, Errors) {
             toString(parseCachePruningPolicy("cache_size=foo%").takeError()));
   EXPECT_EQ("'101' must be between 0 and 100",
             toString(parseCachePruningPolicy("cache_size=101%").takeError()));
+  EXPECT_EQ(
+      "'foo' not an integer",
+      toString(parseCachePruningPolicy("cache_size_bytes=foo").takeError()));
+  EXPECT_EQ(
+      "'foo' not an integer",
+      toString(parseCachePruningPolicy("cache_size_bytes=foom").takeError()));
   EXPECT_EQ("Unknown key: 'foo'",
             toString(parseCachePruningPolicy("foo=bar").takeError()));
 }