]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Reduced startup time with large rock cache_dirs (#634)
authorEduard Bagdasaryan <eduard.bagdasaryan@measurement-factory.com>
Tue, 19 May 2020 17:01:40 +0000 (17:01 +0000)
committerAmos Jeffries <yadij@users.noreply.github.com>
Fri, 22 May 2020 10:42:29 +0000 (22:42 +1200)
... addressing an old TODO.

Before scanning the disks to find the actual entries, the old rock
cache_dir initialization code had to populate its index as if all disk
slots were available and then remove all the added entries. Those two
wasteful operations took ~1.5 seconds for a 200GB disk before the
PageStack ABA bug was fixed in a586085. With the tree-based fix, that
time increased to ~15 seconds. The delay is completely gone now,
reducing the total index initialization time (for a 200GB disk) down to
a second.

src/MemStore.cc
src/fs/rock/RockSwapDir.cc
src/ipc/mem/PagePool.cc
src/ipc/mem/PageStack.cc
src/ipc/mem/PageStack.h

index 5b38b9cdaa85cdb898d102076086510ad5bde477..7c77002fda420456818a70f27de889ac598bf9c9 100644 (file)
@@ -1008,10 +1008,14 @@ MemStoreRr::create()
 
     const int64_t entryLimit = MemStore::EntryLimit();
     assert(entryLimit > 0);
+
+    Ipc::Mem::PageStack::Config spaceConfig;
+    spaceConfig.poolId = Ipc::Mem::PageStack::IdForMemStoreSpace(),
+    spaceConfig.pageSize = 0; // the pages are stored in Ipc::Mem::Pages
+    spaceConfig.capacity = entryLimit;
+    spaceConfig.createFull = true; // all pages are initially available
     Must(!spaceOwner);
-    spaceOwner = shm_new(Ipc::Mem::PageStack)(SpaceLabel,
-                 Ipc::Mem::PageStack::IdForMemStoreSpace(),
-                 entryLimit, 0);
+    spaceOwner = shm_new(Ipc::Mem::PageStack)(SpaceLabel, spaceConfig);
     Must(!mapOwner);
     mapOwner = MemStoreMap::Init(MapLabel, entryLimit);
     Must(!extrasOwner);
index ac324aac5fc0faa904f831fdb3fa7d20224cac3c..d39efc043913ae22efaa193164579dab0923346d 100644 (file)
@@ -1136,19 +1136,14 @@ void Rock::SwapDirRr::create()
             mapOwners.push_back(mapOwner);
 
             // TODO: somehow remove pool id and counters from PageStack?
+            Ipc::Mem::PageStack::Config config;
+            config.poolId = Ipc::Mem::PageStack::IdForSwapDirSpace(i);
+            config.pageSize = 0; // this is an index of slots on _disk_
+            config.capacity = capacity;
+            config.createFull = false; // Rebuild finds and pushes free slots
             Ipc::Mem::Owner<Ipc::Mem::PageStack> *const freeSlotsOwner =
-                shm_new(Ipc::Mem::PageStack)(sd->freeSlotsPath(),
-                                             Ipc::Mem::PageStack::IdForSwapDirSpace(i),
-                                             capacity,
-                                             0);
+                shm_new(Ipc::Mem::PageStack)(sd->freeSlotsPath(), config);
             freeSlotsOwners.push_back(freeSlotsOwner);
-
-            // TODO: add method to initialize PageStack with no free pages
-            while (true) {
-                Ipc::Mem::PageId pageId;
-                if (!freeSlotsOwner->object()->pop(pageId))
-                    break;
-            }
         }
     }
 }
index 8e81098a3a9ae692605934debbd650df24657be4..2664e71b914a018dd55969a14280eabae06aee7e 100644 (file)
 Ipc::Mem::PagePool::Owner *
 Ipc::Mem::PagePool::Init(const char *const shmId, const Ipc::Mem::PoolId stackId, const unsigned int capacity, const size_t pageSize)
 {
-    return shm_new(PageStack)(shmId, stackId, capacity, pageSize);
+    PageStack::Config config;
+    config.poolId = stackId;
+    config.pageSize = pageSize; // the pages are stored in Ipc::Mem::Pages
+    config.capacity = capacity;
+    config.createFull = true; // all pages are initially available
+    return shm_new(PageStack)(shmId, config);
 }
 
 Ipc::Mem::PagePool::PagePool(const char *const id):
index a2557f700203c1a0193d7b39a5db20bcb11fc320..d0ea201a20bbb5eceb7bd9aba1d079c8786e12d2 100644 (file)
@@ -156,8 +156,6 @@ Ipc::Mem::IdSet::IdSet(const size_type capacity):
     // atomic wrappers in nodes_ must be zero-size. Check the best we can. Once.
     static_assert(sizeof(StoredNode) == sizeof(Node), "atomic locks use no storage");
     assert(StoredNode().is_lock_free());
-
-    makeFullBeforeSharing();
 }
 
 void
@@ -428,12 +426,15 @@ Ipc::Mem::IdSet::MemorySize(const size_type capacity)
 
 /* Ipc::Mem::PageStack */
 
-Ipc::Mem::PageStack::PageStack(const PoolId aPoolId, const PageCount aCapacity, const size_t aPageSize):
-    thePoolId(aPoolId), capacity_(aCapacity), thePageSize(aPageSize),
+Ipc::Mem::PageStack::PageStack(const Config &config):
+    config_(config),
     size_(0),
-    ids_(capacity_)
+    ids_(config_.capacity)
 {
-    size_ = capacity_;
+    if (config.createFull) {
+        ids_.makeFullBeforeSharing();
+        size_ = config_.capacity;
+    }
 }
 
 bool
@@ -441,7 +442,7 @@ Ipc::Mem::PageStack::pop(PageId &page)
 {
     assert(!page);
 
-    if (!capacity_)
+    if (!config_.capacity)
         return false;
 
     IdSet::size_type pageIndex = 0;
@@ -450,10 +451,10 @@ Ipc::Mem::PageStack::pop(PageId &page)
 
     // must decrement after removing the page to avoid underflow
     const auto newSize = --size_;
-    assert(newSize < capacity_);
+    assert(newSize < config_.capacity);
 
     page.number = pageIndex + 1;
-    page.pool = thePoolId;
+    page.pool = config_.poolId;
     debugs(54, 8, page << " size: " << newSize);
     assert(pageIdIsValid(page));
     return true;
@@ -468,7 +469,7 @@ Ipc::Mem::PageStack::push(PageId &page)
 
     // must increment before inserting the page to avoid underflow in pop()
     const auto newSize = ++size_;
-    assert(newSize <= capacity_);
+    assert(newSize <= config_.capacity);
 
     const auto pageIndex = page.number - 1;
     ids_.push(pageIndex);
@@ -480,22 +481,22 @@ Ipc::Mem::PageStack::push(PageId &page)
 bool
 Ipc::Mem::PageStack::pageIdIsValid(const PageId &page) const
 {
-    return page.pool == thePoolId &&
+    return page.pool == config_.poolId &&
            0 < page.number && page.number <= capacity();
 }
 
 size_t
 Ipc::Mem::PageStack::sharedMemorySize() const
 {
-    return SharedMemorySize(thePoolId, capacity_, thePageSize);
+    return SharedMemorySize(config_);
 }
 
 size_t
-Ipc::Mem::PageStack::SharedMemorySize(const PoolId, const PageCount capacity, const size_t pageSize)
+Ipc::Mem::PageStack::SharedMemorySize(const Config &cfg)
 {
     const auto levelsSize = PageId::maxPurpose * sizeof(Levels_t);
-    const size_t pagesDataSize = capacity * pageSize;
-    return StackSize(capacity) + LevelsPaddingSize(capacity) + levelsSize + pagesDataSize;
+    const size_t pagesDataSize = cfg.capacity * cfg.pageSize;
+    return StackSize(cfg.capacity) + pagesDataSize + levelsSize;
 }
 
 size_t
@@ -510,7 +511,7 @@ Ipc::Mem::PageStack::StackSize(const PageCount capacity)
 size_t
 Ipc::Mem::PageStack::stackSize() const
 {
-    return StackSize(capacity_);
+    return StackSize(config_.capacity);
 }
 
 size_t
index 46a3822e068b1703f99cfc98c8f8b40b7c7e3c12..d3b44f74665e6e01d2b6e903ce2b0abc78467d2c 100644 (file)
@@ -116,10 +116,24 @@ public:
     /// the number of (free and/or used) pages in a stack
     typedef unsigned int PageCount;
 
-    PageStack(const PoolId aPoolId, const PageCount aCapacity, const size_t aPageSize);
+    // XXX: poolId, pageSize look misplaced due to messy separation of PagePool
+    // (which should support multiple Segments but does not) and PageStack
+    // (which should not calculate the Segment size but does) duties.
+    /// PageStack construction and SharedMemorySize calculation parameters
+    class Config {
+    public:
+        uint32_t poolId = 0; ///< pool ID
+        size_t pageSize = 0; ///< page size, used to calculate shared memory size
+        PageCount capacity = 0; ///< the maximum number of pages
+
+        /// whether a newly created PageStack should be prefilled with PageIds
+        bool createFull = false;
+    };
 
-    PageCount capacity() const { return capacity_; }
-    size_t pageSize() const { return thePageSize; }
+    explicit PageStack(const Config &);
+
+    PageCount capacity() const { return config_.capacity; }
+    size_t pageSize() const { return config_.pageSize; }
     /// an approximate number of free pages
     PageCount size() const { return size_.load(); }
 
@@ -131,7 +145,7 @@ public:
     bool pageIdIsValid(const PageId &page) const;
 
     /// total shared memory size required to share
-    static size_t SharedMemorySize(const PoolId aPoolId, const PageCount capacity, const size_t pageSize);
+    static size_t SharedMemorySize(const Config &);
     size_t sharedMemorySize() const;
 
     /// shared memory size required only by PageStack, excluding
@@ -141,7 +155,7 @@ public:
 
     /// \returns the number of padding bytes to align PagePool::theLevels array
     static size_t LevelsPaddingSize(const PageCount capacity);
-    size_t levelsPaddingSize() const { return LevelsPaddingSize(capacity_); }
+    size_t levelsPaddingSize() const { return LevelsPaddingSize(config_.capacity); }
 
     /**
      * The following functions return PageStack IDs for the corresponding
@@ -157,12 +171,7 @@ public:
     static PoolId IdForSwapDirSpace(const int dirIdx) { return 900 + dirIdx + 1; }
 
 private:
-    // XXX: theFoo members look misplaced due to messy separation of PagePool
-    // (which should support multiple Segments but does not) and PageStack
-    // (which should not calculate the Segment size but does) duties.
-    const PoolId thePoolId; ///< pool ID
-    const PageCount capacity_; ///< the maximum number of pages
-    const size_t thePageSize; ///< page size, used to calculate shared memory size
+    const Config config_;
     /// a lower bound for the number of free pages (for debugging purposes)
     std::atomic<PageCount> size_;