From f6d2db4b93a93a0fb1f1c980b42f6cfa94e6e216 Mon Sep 17 00:00:00 2001 From: "Daniel R. Carvalho" Date: Mon, 17 Jun 2019 17:41:09 +0200 Subject: [PATCH] mem-cache: Allow moving data contractions Data contractions happen when a block passes from a less compressed (e.g., uncompressed) to a more compressed (e.g., compressed) state. Some compaction methods enforce that a block can only be allocated in a location matches an exact compression factor, thus on data contractions such blocks must be moved to another location, or they must be padded to fake a bigger size. For compaction methods that do not have that limitation, performance can be improved if the contracted block is moved to co-allocate with another existing entry, since it frees up an entry. Change-Id: I302bc561b897f9d3ce1426331fe4b5c2df76f4b5 Signed-off-by: Daniel R. Carvalho Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/36578 Reviewed-by: Nikos Nikoleris Maintainer: Nikos Nikoleris Tested-by: kokoro --- src/mem/cache/Cache.py | 5 ++++ src/mem/cache/base.cc | 61 +++++++++++++++++++++++++++++++----------- src/mem/cache/base.hh | 14 ++++++++++ 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/src/mem/cache/Cache.py b/src/mem/cache/Cache.py index 149f48542..a99d549c0 100644 --- a/src/mem/cache/Cache.py +++ b/src/mem/cache/Cache.py @@ -105,6 +105,11 @@ class BaseCache(ClockedObject): compressor = Param.BaseCacheCompressor(NULL, "Cache compressor.") replace_expansions = Param.Bool(True, "Apply replacement policy to " \ "decide which blocks should be evicted on a data expansion") + # When a block passes from uncompressed to compressed, it may become + # co-allocatable with another existing entry of the same superblock, + # so try move the block to co-allocate it + move_contractions = Param.Bool(True, "Try to co-allocate blocks that " + "contract") sequential_access = Param.Bool(False, "Whether to access tags and data sequentially") diff --git a/src/mem/cache/base.cc b/src/mem/cache/base.cc index b128b5c33..78e3f3e36 100644 --- a/src/mem/cache/base.cc +++ b/src/mem/cache/base.cc @@ -100,6 +100,7 @@ BaseCache::BaseCache(const BaseCacheParams &p, unsigned blk_size) clusivity(p.clusivity), isReadOnly(p.is_read_only), replaceExpansions(p.replace_expansions), + moveContractions(p.move_contractions), blocked(0), order(0), noTargetMSHR(nullptr), @@ -862,15 +863,31 @@ BaseCache::updateCompressionData(CacheBlk *&blk, const uint64_t* data, const bool is_co_allocatable = superblock->isCompressed(compression_blk) && superblock->canCoAllocate(compression_size); - // If block was compressed, possibly co-allocated with other blocks, and - // cannot be co-allocated anymore, one or more blocks must be evicted to - // make room for the expanded block + // If compressed size didn't change enough to modify its co-allocatability + // there is nothing to do. Otherwise we may be facing a data expansion + // (block passing from more compressed to less compressed state), or a + // data contraction (less to more). const bool was_compressed = compression_blk->isCompressed(); + bool is_data_expansion = false; + bool is_data_contraction = false; + string op_name = ""; if (was_compressed && !is_co_allocatable) { + is_data_expansion = true; + op_name = "expansion"; + } else if (moveContractions && !was_compressed && is_co_allocatable) { + is_data_contraction = true; + op_name = "contraction"; + } + + // If block changed compression state, it was possibly co-allocated with + // other blocks and cannot be co-allocated anymore, so one or more blocks + // must be evicted to make room for the expanded/contracted block + std::vector evict_blks; + if (is_data_expansion || is_data_contraction) { std::vector evict_blks; bool victim_itself = false; CacheBlk *victim = nullptr; - if (replaceExpansions) { + if (replaceExpansions || is_data_contraction) { victim = tags->findVictim(regenerateBlkAddr(blk), blk->isSecure(), compression_size, evict_blks); @@ -889,8 +906,8 @@ BaseCache::updateCompressionData(CacheBlk *&blk, const uint64_t* data, } // Print victim block's information - DPRINTF(CacheRepl, "Data expansion replacement victim: %s\n", - victim->print()); + DPRINTF(CacheRepl, "Data %s replacement victim: %s\n", + op_name, victim->print()); } else { // If we do not move the expanded block, we must make room for // the expansion to happen, so evict every co-allocated block @@ -908,12 +925,10 @@ BaseCache::updateCompressionData(CacheBlk *&blk, const uint64_t* data, return false; } - // Update the number of data expansions - stats.dataExpansions++; - DPRINTF(CacheComp, "Data expansion: expanding [%s] from %d to %d bits" - "\n", blk->print(), prev_size, compression_size); + DPRINTF(CacheComp, "Data %s: [%s] from %d to %d bits\n", + op_name, blk->print(), prev_size, compression_size); - if (!victim_itself && replaceExpansions) { + if (!victim_itself && (replaceExpansions || is_data_contraction)) { // Move the block's contents to the invalid block so that it now // co-allocates with the other existing superblock entry tags->moveBlock(blk, victim); @@ -922,15 +937,27 @@ BaseCache::updateCompressionData(CacheBlk *&blk, const uint64_t* data, } } - // We always store compressed blocks when possible - if (is_co_allocatable) { - compression_blk->setCompressed(); - } else { - compression_blk->setUncompressed(); + // Update the number of data expansions/contractions + if (is_data_expansion) { + stats.dataExpansions++; + } else if (is_data_contraction) { + stats.dataContractions++; } + compression_blk->setSizeBits(compression_size); compression_blk->setDecompressionLatency(decompression_lat); + if (is_data_expansion || is_data_contraction) { + // If contracting data, for sure data is compressed. If expanding, + // both situations can arise. When no contraction or expansion happens + // block keeps its old state + if (is_co_allocatable) { + compression_blk->setCompressed(); + } else { + compression_blk->setUncompressed(); + } + } + return true; } @@ -2088,6 +2115,7 @@ BaseCache::CacheStats::CacheStats(BaseCache &c) replacements(this, "replacements", "number of replacements"), dataExpansions(this, "data_expansions", "number of data expansions"), + dataContractions(this, "data_contractions", "number of data contractions"), cmd(MemCmd::NUM_MEM_CMDS) { for (int idx = 0; idx < MemCmd::NUM_MEM_CMDS; ++idx) @@ -2307,6 +2335,7 @@ BaseCache::CacheStats::regStats() } dataExpansions.flags(nozero | nonan); + dataContractions.flags(nozero | nonan); } void diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh index 62c2953f2..8dad1b7f7 100644 --- a/src/mem/cache/base.hh +++ b/src/mem/cache/base.hh @@ -901,6 +901,14 @@ class BaseCache : public ClockedObject */ const bool replaceExpansions; + /** + * Similar to data expansions, after a block improves its compression, + * it may need to be moved elsewhere compatible with the new compression + * factor, or, if not required by the compaction method, it may be moved + * to co-allocate with an existing block and thus free an entry. + */ + const bool moveContractions; + /** * Bit vector of the blocking reasons for the access path. * @sa #BlockedCause @@ -1074,6 +1082,12 @@ class BaseCache : public ClockedObject /** Number of data expansions. */ Stats::Scalar dataExpansions; + /** + * Number of data contractions (blocks that had their compression + * factor improved). + */ + Stats::Scalar dataContractions; + /** Per-command statistics */ std::vector> cmd; } stats; -- 2.30.2