Daniel Carvalho has uploaded this change for review. ( https://gem5-review.googlesource.com/c/public/gem5/+/12087

Change subject: mem-cache: Handle data expansion
......................................................................

mem-cache: Handle data expansion

When a block in compressed form is overwriten, it may change
its size. If the new compressed size is bigger, and the total
size becomes bigger than the block size, one or more blocks
will have to be evicted. This is called data expansion, or
fat writes.

This patch adds the functionality to handle data expansions
by evicting the co-allocated blocks to make room for an
expanded block.

As a side effect, satisfyRequest() must now return writebacks.

Change-Id: I0bd77bf6446bfae336889940b2f75d6f0c87e533
---
M src/mem/cache/base.cc
M src/mem/cache/base.hh
M src/mem/cache/cache.cc
M src/mem/cache/cache.hh
M src/mem/cache/noncoherent_cache.cc
M src/mem/cache/noncoherent_cache.hh
M src/mem/cache/tags/compressed_tags.cc
M src/mem/cache/tags/compressed_tags.hh
8 files changed, 184 insertions(+), 18 deletions(-)



diff --git a/src/mem/cache/base.cc b/src/mem/cache/base.cc
index 159281c..5794fbc 100644
--- a/src/mem/cache/base.cc
+++ b/src/mem/cache/base.cc
@@ -827,7 +827,46 @@
 }

 void
-BaseCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool)
+BaseCache::updateCompressionData(const uint64_t* data, CacheBlk *blk,
+                                 PacketList &writebacks)
+{
+    // Only need to update compression data if there is a compressor.
+    // If block has been updated to the same value there is no compression
+    // value change and we can skip it for simulation performance
+    if (!compressor || (std::memcmp(data, blk->data, blkSize) == 0)) {
+        return;
+    }
+
+    // The compressor is called to compress the updated data, so that its
+    // metadata can be updated.
+    std::size_t compression_size = 0;
+    Cycles compression_lat = Cycles(0);
+    Cycles decompression_lat = Cycles(0);
+    compressor->compress(data, compression_lat, decompression_lat,
+                         compression_size);
+
+ // If block's compression factor increased, it may not be co-allocatable + // anymore. If so, some blocks might need to be evicted to make room for
+    // the bigger block
+    std::vector<CacheBlk*> evict_blks =
+ static_cast<CompressedTags*>(tags)->updateBlock(blk, compression_size,
+                                                        decompression_lat);
+
+    // Evict valid blocks
+    for (const auto& blk : evict_blks) {
+        if (blk->isValid()) {
+            if (blk->wasPrefetched()) {
+                unusedPrefetches++;
+            }
+
+            evictBlock(blk, writebacks);
+        }
+    }
+}
+
+void
+BaseCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, PacketList &writebacks,
+                          bool, bool)
 {
     assert(pkt->isRequest());

@@ -844,9 +883,9 @@
     // Check RMW operations first since both isRead() and
     // isWrite() will be true for them
     if (pkt->cmd == MemCmd::SwapReq) {
-        // @todo
         // Check for data expansion on the block. The packet does not need
         // to be checked, as it contains uncompressed data.
+ updateCompressionData(pkt->getConstPtr<uint64_t>(), blk, writebacks);

         if (pkt->isAtomicOp()) {
             // extract data from cache and save it into the data field in
@@ -872,7 +911,9 @@
         assert(blk->isWritable());
// Write or WriteLine at the first cache with block in writable state
         if (blk->checkWrite(pkt)) {
-            // @todo Update compression data
+            // Update compression data
+            updateCompressionData(pkt->getConstPtr<uint64_t>(), blk,
+                                  writebacks);

             pkt->writeDataToBlock(blk->data, blkSize);
         }
@@ -1024,7 +1065,9 @@

             blk->status |= (BlkValid | BlkReadable);
         } else {
-            // @todo Update compression data
+            // Update compression data
+            updateCompressionData(pkt->getConstPtr<uint64_t>(), blk,
+                                  writebacks);
         }

         // only mark the block dirty if we got a writeback command,
@@ -1086,7 +1129,9 @@
                 blk->status |= (BlkValid | BlkReadable);
             }
         } else {
-            // @todo Update compression data
+            // Update compression data
+            updateCompressionData(pkt->getConstPtr<uint64_t>(), blk,
+                                  writebacks);
         }

         // at this point either this is a writeback or a write-through
@@ -1113,7 +1158,7 @@
                        blk->isReadable())) {
         // OK to satisfy access
         incHitCount(pkt);
-        satisfyRequest(pkt, blk);
+        satisfyRequest(pkt, blk, writebacks);
         maintainClusivity(pkt->fromCache(), blk);

         return true;
diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh
index 3b432bc..447fddc 100644
--- a/src/mem/cache/base.hh
+++ b/src/mem/cache/base.hh
@@ -613,16 +613,30 @@
     EventFunctionWrapper writebackTempBlockAtomicEvent;

     /**
+ * Update block's compression data when it is overwriten. This may generate
+     * evictions due to data expansions.
+     * @sa CompressedTags
+     *
+     * @param data A pointer to the data to be compressed (blk's new data).
+     * @param blk The block to be overwriten.
+     * @param writebacks List for any writebacks that need to be performed.
+     */
+    void updateCompressionData(const uint64_t* data, CacheBlk *blk,
+                               PacketList &writebacks);
+
+    /**
      * Perform any necessary updates to the block and perform any data
      * exchange between the packet and the block. The flags of the
      * packet are also set accordingly.
      *
      * @param pkt Request packet from upstream that hit a block
      * @param blk Cache block that the packet hit
+     * @param writebacks List for any writebacks that need to be performed.
      * @param deferred_response Whether this request originally missed
      * @param pending_downgrade Whether the writable flag is to be removed
      */
     virtual void satisfyRequest(PacketPtr pkt, CacheBlk *blk,
+                                PacketList &writebacks,
                                 bool deferred_response = false,
                                 bool pending_downgrade = false);

diff --git a/src/mem/cache/cache.cc b/src/mem/cache/cache.cc
index 9b16129..29e2ec1 100644
--- a/src/mem/cache/cache.cc
+++ b/src/mem/cache/cache.cc
@@ -78,10 +78,10 @@
 }

 void
-Cache::satisfyRequest(PacketPtr pkt, CacheBlk *blk,
+Cache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, PacketList &writebacks,
                       bool deferred_response, bool pending_downgrade)
 {
-    BaseCache::satisfyRequest(pkt, blk);
+    BaseCache::satisfyRequest(pkt, blk, writebacks);

     if (pkt->isRead()) {
         // determine if this read is from a (coherent) cache or not
@@ -624,14 +624,14 @@
                                  allocOnFill(pkt->cmd));
                 assert(blk != NULL);
                 is_invalidate = false;
-                satisfyRequest(pkt, blk);
+                satisfyRequest(pkt, blk, writebacks);
             } else if (bus_pkt->isRead() ||
                        bus_pkt->cmd == MemCmd::UpgradeResp) {
                 // we're updating cache state to allow us to
                 // satisfy the upstream request from the cache
                 blk = handleFill(bus_pkt, blk, writebacks,
                                  allocOnFill(pkt->cmd));
-                satisfyRequest(pkt, blk);
+                satisfyRequest(pkt, blk, writebacks);
                 maintainClusivity(pkt->fromCache(), blk);
             } else {
                 // we're satisfying the upstream request without
@@ -719,7 +719,8 @@
             }

             if (blk && blk->isValid() && !mshr->isForward) {
- satisfyRequest(tgt_pkt, blk, true, mshr->hasPostDowngrade());
+                satisfyRequest(tgt_pkt, blk, writebacks, true,
+                               mshr->hasPostDowngrade());

                 // How many bytes past the first request is this one
                 int transfer_offset =
diff --git a/src/mem/cache/cache.hh b/src/mem/cache/cache.hh
index 32752a5..5bf1458 100644
--- a/src/mem/cache/cache.hh
+++ b/src/mem/cache/cache.hh
@@ -117,7 +117,7 @@

     Tick recvAtomicSnoop(PacketPtr pkt) override;

-    void satisfyRequest(PacketPtr pkt, CacheBlk *blk,
+ void satisfyRequest(PacketPtr pkt, CacheBlk *blk, PacketList& writebacks,
                         bool deferred_response = false,
                         bool pending_downgrade = false) override;

diff --git a/src/mem/cache/noncoherent_cache.cc b/src/mem/cache/noncoherent_cache.cc
index 5073837..4f912ca 100644
--- a/src/mem/cache/noncoherent_cache.cc
+++ b/src/mem/cache/noncoherent_cache.cc
@@ -70,13 +70,14 @@
 }

 void
-NoncoherentCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool)
+NoncoherentCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk,
+                                 PacketList& writebacks, bool, bool)
 {
     // As this a non-coherent cache located below the point of
     // coherency, we do not expect requests that are typically used to
     // keep caches coherent (e.g., InvalidateReq or UpdateReq).
     assert(pkt->isRead() || pkt->isWrite());
-    BaseCache::satisfyRequest(pkt, blk);
+    BaseCache::satisfyRequest(pkt, blk, writebacks);
 }

 bool
@@ -198,7 +199,7 @@
blk = handleFill(bus_pkt, blk, writebacks, allocOnFill(bus_pkt->cmd));
         assert(blk);
     }
-    satisfyRequest(pkt, blk);
+    satisfyRequest(pkt, blk, writebacks);

     maintainClusivity(true, blk);

@@ -261,7 +262,7 @@
             // packet comes from it, charged on headerDelay.
             completion_time = pkt->headerDelay;

-            satisfyRequest(tgt_pkt, blk);
+            satisfyRequest(tgt_pkt, blk, writebacks);

             // How many bytes past the first request is this one
             int transfer_offset;
diff --git a/src/mem/cache/noncoherent_cache.hh b/src/mem/cache/noncoherent_cache.hh
index 09012ba..2a5a3dd 100644
--- a/src/mem/cache/noncoherent_cache.hh
+++ b/src/mem/cache/noncoherent_cache.hh
@@ -109,7 +109,7 @@

     void functionalAccess(PacketPtr pkt, bool from_cpu_side) override;

-    void satisfyRequest(PacketPtr pkt, CacheBlk *blk,
+ void satisfyRequest(PacketPtr pkt, CacheBlk *blk, PacketList& writebacks,
                         bool deferred_response = false,
                         bool pending_downgrade = false) override;

diff --git a/src/mem/cache/tags/compressed_tags.cc b/src/mem/cache/tags/compressed_tags.cc
index d7f4e39..abb4398 100644
--- a/src/mem/cache/tags/compressed_tags.cc
+++ b/src/mem/cache/tags/compressed_tags.cc
@@ -209,8 +209,10 @@
     const SuperblockBlk* superblock =
static_cast<const SuperblockBlk*>(compression_blk->getSectorBlock());

-    // @todo
// Set compression data. We always store compressed blocks when possible
+    compression_blk->setCompressionData(canCoAllocate(superblock,
+                                        compressed_size), compressed_size,
+                                        decompression_lat);

     // When a block is inserted, the tag is only a newly used tag if the
     // superblock was not previously present in the cache.
@@ -227,6 +229,67 @@
     }
 }

+std::vector<CacheBlk*>
+CompressedTags::updateBlock(CacheBlk* blk,
+                            const std::size_t compression_size,
+                            const Cycles decompression_lat)
+{
+    // Make sure block is valid
+    assert(blk->isValid());
+
+    // We are handling a compressed blk
+    CompressionBlk* comp_blk = static_cast<CompressionBlk*>(blk);
+
+    // Get superblock of the given block
+    const SuperblockBlk* superblock = static_cast<const SuperblockBlk*>(
+                                          comp_blk->getSectorBlock());
+
+    // Get previous compressed size
+    const std::size_t prev_size = comp_blk->getSize();
+
+    // Check if new size is co-allocatable
+ const bool is_co_allocatable = canCoAllocate(superblock, compression_size);
+
+    // Update cache block with new compression data
+    comp_blk->setCompressionData(is_co_allocatable, compression_size,
+                                 decompression_lat);
+
+ // If block cannot be co-allocated one or more blocks must be evicted to + // make room for the expanded block. As of now we decide to evict the co- + // allocated blocks to make room for the expansion, but other approaches
+    // that take the replacement data of the superblock into account may
+    // generate better results
+    std::vector<CacheBlk*> evict_blks;
+    if (!is_co_allocatable) {
+        // Get all co-allocated blocks
+        for (const auto& sub_blk : superblock->blks) {
+            if (sub_blk->isValid() && (comp_blk != sub_blk)) {
+                evict_blks.push_back(sub_blk);
+            }
+        }
+
+        // Update the number of data expansions
+        dataExpansions++;
+
+ DPRINTF(CacheComp, "data expansion: expanding %#llx (%s) from %d to" \ + " %d\n", regenerateBlkAddr(blk), blk->isSecure() ? "s" : "ns",
+                prev_size, compression_size);
+    }
+
+    return evict_blks;
+}
+
+void
+CompressedTags::regStats()
+{
+    BaseTags::regStats();
+
+    dataExpansions
+        .name(name() + ".dataExpansions")
+        .desc("number of data expansions")
+        ;
+}
+
 CompressedTags *
 CompressedTagsParams::create()
 {
diff --git a/src/mem/cache/tags/compressed_tags.hh b/src/mem/cache/tags/compressed_tags.hh
index 550588a..47d9646 100644
--- a/src/mem/cache/tags/compressed_tags.hh
+++ b/src/mem/cache/tags/compressed_tags.hh
@@ -38,6 +38,7 @@

 #include <vector>

+#include "base/statistics.hh"
 #include "mem/cache/superblock_blk.hh"
 #include "mem/cache/tags/sector_tags.hh"

@@ -77,6 +78,18 @@
     /** The cache superblocks. */
     std::vector<SuperblockBlk> superblockBlks;

+    /**
+     * @defgroup CompressionStats Compression specific statistics.
+     * @{
+     */
+
+    /** Number of data expansions. */
+    Stats::Scalar dataExpansions;
+
+    /**
+     * @}
+     */
+
   public:
     /** Convenience typedef. */
      typedef CompressedTagsParams Params;
@@ -137,6 +150,35 @@
                      const int src_master_ID, const uint32_t task_ID,
                      const std::size_t compressed_size,
                      const Cycles decompression_lat, CacheBlk *blk);
+
+    /**
+ * When a block is overwriten, its compression information must be updated, + * and it may need to be recompressed. If the compression size changes, the + * block may either become smaller, in which case there are no side effect, + * or bigger (data expansion; fat write), in which case the block might not + * fit in its current location anymore. If that happens, there are usually
+     * two options to be taken:
+     *
+ * - The co-allocated blocks must be evicted to make room for this block.
+     *   Simpler, but ignores replacement data.
+     * - The block itself is moved elsewhere (used in policies where the CF
+     *   determines the location of the block).
+     *
+     * This implementation uses the first approach.
+     *
+     * @param blk A valid block to be updated.
+ * @param compression_size The new size, in bits, of the compressed block.
+     * @param decompression_lat The decompression latency of the block.
+ * @return The blocks to be evicted due to this overwrite, if they exist.
+     */
+    std::vector<CacheBlk*> updateBlock(CacheBlk* blk,
+                                      const std::size_t compression_size,
+                                      const Cycles decompression_lat);
+
+    /**
+     * Register local statistics.
+     */
+    void regStats() override;
 };

 #endif //__MEM_CACHE_TAGS_COMPRESSED_TAGS_HH__

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/12087
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: master
Gerrit-Change-Id: I0bd77bf6446bfae336889940b2f75d6f0c87e533
Gerrit-Change-Number: 12087
Gerrit-PatchSet: 1
Gerrit-Owner: Daniel Carvalho <[email protected]>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list
[email protected]
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to