tags->tagsInit();
if (prefetcher)
prefetcher->setCache(this);
+ if (compressor)
+ compressor->setCache(this);
}
BaseCache::~BaseCache()
from m5.params import *
from m5.proxy import *
-from m5.SimObject import SimObject
+from m5.SimObject import *
+
+from m5.objects.IndexingPolicies import *
+from m5.objects.ReplacementPolicies import *
class BaseCacheCompressor(SimObject):
type = 'BaseCacheCompressor'
dictionary_size = 2
+class FrequentValuesCompressor(BaseCacheCompressor):
+ type = 'FrequentValuesCompressor'
+ cxx_class = 'Compressor::FrequentValues'
+ cxx_header = "mem/cache/compressors/frequent_values.hh"
+
+ chunk_size_bits = 32
+ code_generation_ticks = Param.Unsigned(10000, "Number of elapsed " \
+ "ticks until the samples are analyzed and their codes are generated.")
+ # @todo The width of a counter width is determined by the maximum
+ # number of times a given value appears in the cache - i.e.,
+ # log2(cache_size/chunk_size_bits))".
+ counter_bits = Param.Unsigned(18, "Number of bits per frequency counter.")
+ max_code_length = Param.Unsigned(18, "Maximum number of bits in a "
+ "codeword. If 0, table indices are not encoded.")
+ num_samples = Param.Unsigned(100000, "Number of samples that must be " \
+ "taken before compression is effectively used.")
+ check_saturation = Param.Bool(False, "Whether the counters should be " \
+ "manipulated in case of saturation.")
+
+ vft_assoc = Param.Int(16, "Associativity of the VFT.")
+ vft_entries = Param.MemorySize("1024", "Number of entries of the VFT.")
+ vft_indexing_policy = Param.BaseIndexingPolicy(
+ SetAssociative(entry_size = 1, assoc = Parent.vft_assoc,
+ size = Parent.vft_entries), "Indexing policy of the VFT.")
+ vft_replacement_policy = Param.BaseReplacementPolicy(LFURP(),
+ "Replacement policy of the VFT.")
+
+ comp_chunks_per_cycle = 1
+ comp_extra_latency = 1
+ decomp_chunks_per_cycle = 1
+ decomp_extra_latency = 0
+
class MultiCompressor(BaseCacheCompressor):
type = 'MultiCompressor'
cxx_class = 'Compressor::Multi'
Source('cpack.cc')
Source('fpc.cc')
Source('fpcd.cc')
+Source('frequent_values.cc')
Source('multi.cc')
Source('perfect.cc')
Source('repeated_qwords.cc')
#include "base/trace.hh"
#include "debug/CacheComp.hh"
+#include "mem/cache/base.hh"
#include "mem/cache/tags/super_blk.hh"
#include "params/BaseCacheCompressor.hh"
compExtraLatency(p.comp_extra_latency),
decompChunksPerCycle(p.decomp_chunks_per_cycle),
decompExtraLatency(p.decomp_extra_latency),
- stats(*this)
+ cache(nullptr), stats(*this)
{
fatal_if(64 % chunkSizeBits,
"64 must be a multiple of the chunk granularity.");
fatal_if(blkSize < sizeThreshold, "Compressed data must fit in a block");
}
+void
+Base::setCache(BaseCache *_cache)
+{
+ assert(!cache);
+ cache = _cache;
+}
+
std::vector<Base::Chunk>
Base::toChunks(const uint64_t* data) const
{
#include "base/types.hh"
#include "sim/sim_object.hh"
+class BaseCache;
class CacheBlk;
struct BaseCacheCompressorParams;
*/
const Cycles decompExtraLatency;
+ /** Pointer to the parent cache. */
+ BaseCache* cache;
+
struct BaseStats : public Stats::Group
{
const Base& compressor;
Base(const Params &p);
virtual ~Base() = default;
+ /** The cache can only be set once. */
+ virtual void setCache(BaseCache *_cache);
+
/**
* Apply the compression process to the cache line. Ignores compression
* cycles.
--- /dev/null
+# -*- mode:python -*-
+
+# Copyright (c) 2020 Inria
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('*')
+
+Source('huffman.cc')
--- /dev/null
+/*
+ * Copyright (c) 2019, 2020 Inria
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEM_CACHE_COMPRESSORS_ENCODERS_BASE_HH__
+#define __MEM_CACHE_COMPRESSORS_ENCODERS_BASE_HH__
+
+#include <cstdint>
+
+namespace Compressor {
+namespace Encoder {
+
+struct Code
+{
+ /** Only the LSB of the code are relevant. */
+ uint64_t code;
+ /** Number of bits in the code. */
+ unsigned length;
+};
+
+/**
+ * Base class for encoders. The goal of encoders is to provide an alternative
+ * representation to values, ideally shorter than the value. The alternative
+ * representation is called a code.
+ */
+class Base
+{
+ public:
+ Base() {}
+ virtual ~Base() = default;
+
+ /**
+ * The function responsible for the generation of the alternative value.
+ * If the size of the returning Code is greater than the maximum undelying
+ * type's size (e.g., 64 bits) the encoding results should be discarded.
+ *
+ * @param The value to be encoded.
+ * @return The encoded value.
+ */
+ virtual Code encode(const uint64_t val) const = 0;
+
+ /**
+ * Decode a value.
+ * @sa encode()
+ *
+ * @param code The encoded value.
+ * @return The original value.
+ */
+ virtual uint64_t decode(const uint64_t code) const = 0;
+};
+
+} // namespace Encoder
+} // namespace Compressor
+
+#endif //__MEM_CACHE_COMPRESSORS_ENCODERS_BASE_HH__
--- /dev/null
+/*
+ * Copyright (c) 2019, 2020 Inria
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mem/cache/compressors/encoders/huffman.hh"
+
+#include <cassert>
+
+#include "base/logging.hh"
+
+namespace Compressor {
+namespace Encoder {
+
+Huffman::Huffman(uint64_t max_code_length)
+ : Base(), maxCodeLength(max_code_length)
+{
+ fatal_if(maxCodeLength > 64,
+ "Code length cannot surpass its underlying container");
+}
+
+void
+Huffman::sample(uint64_t value, uint64_t frequency)
+{
+ if (frequency != 0) {
+ trees.push(new Node(value, frequency));
+ }
+}
+
+std::unique_ptr<Huffman::Node>
+Huffman::buildTree()
+{
+ // Construct tree by assigning left and right nodes. The left path leads
+ // to the most frequent values
+ while (trees.size() > 1) {
+ Node* left = trees.top();
+ trees.pop();
+
+ Node* right = trees.top();
+ trees.pop();
+
+ Node* parent = new Node(left, right);
+ trees.push(parent);
+ }
+
+ // All queue entries have been merged into a single entry containing
+ // the tree
+ Node* root = trees.top();
+ trees.pop();
+ return std::unique_ptr<Node>(root);
+}
+
+void
+Huffman::generateCodeMaps()
+{
+ valueToCode.clear();
+ codeToValue.clear();
+ generateCodes(buildTree().get(), Code());
+}
+
+void
+Huffman::generateCodes(const Node* node, const Code& current_code)
+{
+ // Drop all entries with length greater than maxCodeLength
+ if (current_code.length > maxCodeLength) {
+ return;
+ }
+
+ if (node->isLeaf()) {
+ valueToCode[node->getValue()] = current_code;
+ codeToValue[current_code.code] = node->getValue();
+ } else {
+ Code right_code = current_code;
+ right_code.code = (right_code.code << 1) + 1;
+ right_code.length++;
+ generateCodes(node->getRightSubTree(), right_code);
+
+ Code left_code = current_code;
+ left_code.code = left_code.code << 1;
+ left_code.length++;
+ generateCodes(node->getLeftSubTree(), left_code);
+ }
+}
+
+Code
+Huffman::encode(const uint64_t val) const
+{
+ auto it = valueToCode.find(val);
+ if (it == valueToCode.end()) {
+ // If the value is unknown, generate a dummy code with invalid
+ // length to let the caller know the encoding is invalid
+ Code dummy_code;
+ dummy_code.code = 0;
+ dummy_code.length = 65;
+ return dummy_code;
+ } else {
+ return it->second;
+ }
+}
+
+uint64_t
+Huffman::decode(const uint64_t code) const
+{
+ // A code that does not exist cannot be decoded
+ auto it = codeToValue.find(code);
+ assert(it != codeToValue.end());
+ return it->second;
+}
+
+} // namespace Encoder
+} // namespace Compressor
--- /dev/null
+/*
+ * Copyright (c) 2019, 2020 Inria
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEM_CACHE_COMPRESSORS_ENCODERS_HUFFMAN_HH__
+#define __MEM_CACHE_COMPRESSORS_ENCODERS_HUFFMAN_HH__
+
+#include <cassert>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <queue>
+#include <vector>
+
+#include "mem/cache/compressors/encoders/base.hh"
+
+namespace Compressor {
+namespace Encoder {
+
+/**
+ * This encoder builds a Huffman tree using the frequency of each value to
+ * be encoded.
+ */
+class Huffman : public Base
+{
+ public:
+ Huffman(uint64_t max_code_length);
+ ~Huffman() = default;
+
+ /**
+ * Inserts the value-frequency pair in the tree.
+ *
+ * @param value The value.
+ * @param frequency The value's frequency.
+ */
+ void sample(uint64_t value, uint64_t frequency);
+
+ /** Generation of the code maps. This automatically builds the tree. */
+ void generateCodeMaps();
+
+ Code encode(const uint64_t val) const override;
+ uint64_t decode(const uint64_t code) const override;
+
+ private:
+ /** Node for the Huffman tree. */
+ class Node
+ {
+ private:
+ /** Frequency of the value represented by this node. */
+ const uint64_t _frequency;
+
+ /** Value represented by this node, if this is a leaf node. */
+ const uint64_t _value;
+
+ /** The left tree. */
+ std::unique_ptr<Node> _left;
+
+ /** The right tree. */
+ std::unique_ptr<Node> _right;
+
+ public:
+ /** Initialize node as a leaf node. */
+ Node(uint64_t value, uint64_t frequency)
+ : _frequency(frequency), _value(value), _left(), _right()
+ {
+ }
+
+ /** Initialize node as an internal node. */
+ Node(Node* left, Node* right)
+ : _frequency(left->getFrequency() + right->getFrequency()),
+ _value(0), _left(left), _right(right)
+ {
+ }
+
+ /** Getter for the frequency counter. */
+ uint64_t getFrequency() const { return _frequency; }
+
+ /**
+ * Determine if the node is a leaf node by checking if it does not
+ * have sub-trees.
+ *
+ * @return Wether the node is a leaf node.
+ */
+ bool
+ isLeaf() const
+ {
+ return (_left == nullptr) && (_right == nullptr);
+ }
+
+ /**
+ * Get the leaf's value.
+ *
+ * @return The leaf's value.
+ */
+ uint64_t
+ getValue() const
+ {
+ assert(isLeaf());
+ return _value;
+ }
+
+ const Node* getLeftSubTree() const { return _left.get(); }
+ const Node* getRightSubTree() const { return _right.get(); }
+ };
+
+ /**
+ * Maximum number of bits in a codeword. If a codeword requires more
+ * than this amount of bits, its respective value is discarded.
+ */
+ const unsigned maxCodeLength;
+
+ /**
+ * Table containing the codewords and their respective lengths. Some
+ * entries are discarded due to their lengths being too big.
+ */
+ std::map<uint64_t, Code> valueToCode;
+ std::map<uint64_t, uint64_t> codeToValue;
+
+ /**
+ * Entries are not inserted directly into the tree. First they are sorted
+ * based on their frequencies.
+ */
+ struct NodeComparator
+ {
+ bool
+ operator()(const Node* lhs, const Node* rhs) const
+ {
+ return lhs->getFrequency() > rhs->getFrequency();
+ }
+ };
+ std::priority_queue<Node*, std::vector<Node*>, NodeComparator> trees;
+
+ /**
+ * Build a Huffman tree using the values and their respective
+ * frequencies, which have been informed through the insertion
+ * function.
+ *
+ * @return A pointer to the root of the tree.
+ */
+ std::unique_ptr<Node> buildTree();
+
+ /**
+ * Recursive function that generates the huffman codes based on
+ * the tree provided. The generated codes are added to the code
+ * map structure.
+ *
+ * @param node The node being analyzed.
+ * @param current_code The code so far.
+ */
+ void generateCodes(const Node* node, const Code& current_code);
+};
+
+} // namespace Encoder
+} // namespace Compressor
+
+#endif //__MEM_CACHE_COMPRESSORS_ENCODERS_HUFFMAN_HH__
--- /dev/null
+/*
+ * Copyright (c) 2019-2020 Inria
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "mem/cache/compressors/frequent_values.hh"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/bitfield.hh"
+#include "base/intmath.hh"
+#include "base/logging.hh"
+#include "debug/CacheComp.hh"
+#include "mem/cache/prefetch/associative_set_impl.hh"
+#include "params/FrequentValuesCompressor.hh"
+
+namespace Compressor {
+
+FrequentValues::FrequentValues(const Params &p)
+ : Base(p), useHuffmanEncoding(p.max_code_length != 0),
+ encoder(p.max_code_length), counterBits(p.counter_bits),
+ codeGenerationTicks(p.code_generation_ticks),
+ checkSaturation(p.check_saturation), numVFTEntries(p.vft_entries),
+ numSamples(p.num_samples), takenSamples(0), phase(SAMPLING),
+ VFT(p.vft_assoc, p.vft_entries, p.vft_indexing_policy,
+ p.vft_replacement_policy, VFTEntry(counterBits)),
+ codeGenerationEvent([this]{ phase = COMPRESSING; }, name())
+{
+ fatal_if((numVFTEntries - 1) > mask(chunkSizeBits),
+ "There are more VFT entries than possible values.");
+}
+
+std::unique_ptr<Base::CompressionData>
+FrequentValues::compress(const std::vector<Chunk>& chunks, Cycles& comp_lat,
+ Cycles& decomp_lat)
+{
+ std::unique_ptr<CompData> comp_data =
+ std::unique_ptr<CompData>(new CompData());
+
+ // Compression size
+ std::size_t size = 0;
+
+ // Compress every value sequentially. The compressed values are then
+ // added to the final compressed data.
+ for (const auto& chunk : chunks) {
+ Encoder::Code code;
+ int length = 0;
+ if (phase == COMPRESSING) {
+ VFTEntry* entry = VFT.findEntry(chunk, false);
+
+ // Theoretically, the code would be the index of the entry;
+ // however, there is no practical need to do so, and we simply
+ // use the value instead
+ const unsigned uncompressed_index = uncompressedValue;
+ const unsigned index = entry ? chunk : uncompressed_index;
+
+ // If using an index encoder, apply it
+ if (useHuffmanEncoding) {
+ code = encoder.encode(index);
+
+ if (index == uncompressed_index) {
+ code.length += chunkSizeBits;
+ } else if (code.length > 64) {
+ // If, for some reason, we could not generate an encoding
+ // for the value, generate the uncompressed encoding
+ code = encoder.encode(uncompressed_index);
+ assert(code.length <= 64);
+ code.length += chunkSizeBits;
+ }
+ } else {
+ const unsigned code_size = std::log2(numVFTEntries);
+ if (entry) {
+ code = {index, code_size};
+ } else {
+ code = {uncompressed_index, code_size + chunkSizeBits};
+ }
+ }
+ } else {
+ // Not compressing yet; simply copy the value over
+ code = {chunk, chunkSizeBits};
+ }
+ length += code.length;
+
+ DPRINTF(CacheComp, "Compressed %016x to %016x (Size = %d) "
+ "(Phase: %d)\n", chunk, code.code, length, phase);
+
+ comp_data->compressedValues.emplace_back(code, chunk);
+
+ size += length;
+ }
+
+ // Set final compression size
+ comp_data->setSizeBits(size);
+
+ // Set latencies based on the degree of parallelization, and any extra
+ // latencies due to shifting or packaging
+ comp_lat = Cycles(compExtraLatency +
+ (chunks.size() / compChunksPerCycle));
+ decomp_lat = Cycles(decompExtraLatency +
+ (chunks.size() / decompChunksPerCycle));
+
+ // Return compressed line
+ return comp_data;
+}
+
+void
+FrequentValues::decompress(const CompressionData* comp_data, uint64_t* data)
+{
+ const CompData* casted_comp_data = static_cast<const CompData*>(comp_data);
+
+ // Decompress every entry sequentially
+ std::vector<Chunk> decomp_chunks;
+ for (const auto& comp_chunk : casted_comp_data->compressedValues) {
+ if (phase == COMPRESSING) {
+ if (useHuffmanEncoding) {
+ // Although in theory we have the codeword and have to find
+ // its corresponding value, in order to make life easier we
+ // search for the value and verify that the stored code
+ // matches the table's
+ M5_VAR_USED const Encoder::Code code =
+ encoder.encode(comp_chunk.value);
+
+ // Either the value will be found and the codes match, or the
+ // value will not be found because it is an uncompressed entry
+ assert(((code.length <= 64) &&
+ (code.code == comp_chunk.code.code)) ||
+ (comp_chunk.code.code ==
+ encoder.encode(uncompressedValue).code));
+ } else {
+ // The value at the given VFT entry must match the one stored,
+ // if it is not the uncompressed value
+ assert((comp_chunk.code.code == uncompressedValue) ||
+ VFT.findEntry(comp_chunk.value, false));
+ }
+ }
+
+ decomp_chunks.push_back(comp_chunk.value);
+ DPRINTF(CacheComp, "Decompressed %016x to %016x\n",
+ comp_chunk.code.code, comp_chunk.value);
+ }
+
+ // Concatenate the decompressed words to generate the cache lines
+ fromChunks(decomp_chunks, data);
+}
+
+void
+FrequentValues::sampleValues(const std::vector<uint64_t> &data,
+ bool is_invalidation)
+{
+ const std::vector<Chunk> chunks = toChunks(data.data());
+ for (const Chunk& chunk : chunks) {
+ VFTEntry* entry = VFT.findEntry(chunk, false);
+ bool saturated = false;
+ if (!is_invalidation) {
+ // If a VFT hit, increase new value's counter; otherwise, insert
+ // new value
+ if (!entry) {
+ entry = VFT.findVictim(chunk);
+ assert(entry != nullptr);
+ entry->value = chunk;
+ VFT.insertEntry(chunk, false, entry);
+ } else {
+ VFT.accessEntry(entry);
+ }
+ entry->counter++;
+ saturated = entry->counter.isSaturated();
+ } else {
+ // If a VFT hit, decrease value's counter
+ if (entry) {
+ VFT.accessEntry(entry);
+ entry->counter--;
+ }
+ }
+
+ // If any counter saturates, all counters are shifted right,
+ // resulting in precision loss
+ if (checkSaturation && saturated) {
+ for (auto& entry : VFT) {
+ entry.counter >>= 1;
+ }
+ }
+ }
+
+ takenSamples += chunks.size();
+}
+
+void
+FrequentValues::generateCodes()
+{
+ // We need to find a pseudo value to store uncompressed values as
+ // For that we generate all possible values from 0 to 1 size larger
+ // than the number of real values.
+ std::set<uint64_t> uncompressed_values;
+ for (int i = 0; i < numVFTEntries+1; ++i) {
+ uncompressed_values.insert(uncompressed_values.end(), i);
+ }
+
+ for (const auto& entry : VFT) {
+ // Remove the respective real value from the list of possible
+ // pseudo values for the uncompressed value
+ uncompressed_values.erase(entry.value);
+ }
+
+ // Select the first remaining possible value as the value
+ // representing uncompressed values
+ assert(uncompressed_values.size() >= 1);
+ uncompressedValue = *uncompressed_values.begin();
+ assert(VFT.findEntry(uncompressedValue, false) == nullptr);
+
+ if (useHuffmanEncoding) {
+ // Populate the queue, adding each entry as a tree with one node.
+ // They are sorted such that the value with highest frequency is
+ // the queue's top
+ for (const auto& entry : VFT) {
+ encoder.sample(entry.value, entry.counter);
+ }
+
+ // Insert the uncompressed value in the tree assuming it has the
+ // highest frequency, since it is in fact a group of all the values
+ // not present in the VFT
+ encoder.sample(uncompressedValue, ULLONG_MAX);
+
+ encoder.generateCodeMaps();
+ }
+
+ // Generate the code map and mark the current phase as code generation
+ phase = CODE_GENERATION;
+
+ // Let us know when to change from the code generation phase to the
+ // effective compression phase
+ schedule(codeGenerationEvent, curTick() + codeGenerationTicks);
+}
+
+void
+FrequentValues::probeNotify(const DataUpdate &data_update)
+{
+ // Do not update VFT if not sampling
+ if (phase == SAMPLING) {
+ // If the new data is not present, the notification is due to a
+ // fill; otherwise, sample the old block's contents
+ if (data_update.oldData.size() > 0) {
+ sampleValues(data_update.oldData, true);
+ }
+ // If the new data is not present, the notification is due to an
+ // invalidation; otherwise, sample the new block's contents
+ if (data_update.newData.size() > 0) {
+ sampleValues(data_update.newData, false);
+ }
+
+ // Check if it is done with the sampling phase. If so, generate the
+ // codes that will be used for the compression phase
+ if (takenSamples >= numSamples) {
+ generateCodes();
+ }
+ }
+}
+
+void
+FrequentValues::regProbeListeners()
+{
+ assert(listeners.empty());
+ assert(cache != nullptr);
+ listeners.push_back(new FrequentValuesListener(
+ *this, cache->getProbeManager(), "Data Update"));
+}
+
+void
+FrequentValues::FrequentValuesListener::notify(const DataUpdate &data_update)
+{
+ parent.probeNotify(data_update);
+}
+
+} // namespace Compressor
--- /dev/null
+/*
+ * Copyright (c) 2019-2020 Inria
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEM_CACHE_COMPRESSORS_FREQUENT_VALUES_HH__
+#define __MEM_CACHE_COMPRESSORS_FREQUENT_VALUES_HH__
+
+#include <climits>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "base/sat_counter.hh"
+#include "base/types.hh"
+#include "mem/cache/base.hh"
+#include "mem/cache/compressors/base.hh"
+#include "mem/cache/compressors/encoders/huffman.hh"
+#include "mem/cache/prefetch/associative_set.hh"
+#include "sim/eventq.hh"
+#include "sim/probe/probe.hh"
+
+struct FrequentValuesCompressorParams;
+
+namespace Compressor {
+
+/**
+ * This compressor samples the cache for a while, trying to define the
+ * most frequently used values. When these values are determined, they are
+ * associated to shorter representations (codes). Then the compressor can
+ * start its effective compression phase, in which occurrences of these
+ * values are substituted by their codes.
+ */
+class FrequentValues : public Base
+{
+ private:
+ class CompData;
+
+ using DataUpdate = BaseCache::DataUpdate;
+
+ class FrequentValuesListener : public ProbeListenerArgBase<DataUpdate>
+ {
+ protected:
+ FrequentValues &parent;
+
+ public:
+ FrequentValuesListener(FrequentValues &_parent, ProbeManager *pm,
+ const std::string &name)
+ : ProbeListenerArgBase(pm, name), parent(_parent)
+ {
+ }
+ void notify(const DataUpdate &data_update) override;
+ };
+ std::vector<FrequentValuesListener*> listeners;
+
+ /** Whether Huffman encoding is applied to the VFT indices. */
+ const bool useHuffmanEncoding;
+
+ /** The encoder applied to the VFT indices. */
+ Encoder::Huffman encoder;
+
+ /** Number of bits in the saturating counters. */
+ const int counterBits;
+
+ /** Ticks needed to perform the CODE_GENERATION phase. */
+ const Tick codeGenerationTicks;
+
+ /** Whether an action must be performed when counters saturate. */
+ const bool checkSaturation;
+
+ /** Maximum number of VFT entries, and thus of codewords too. */
+ const unsigned numVFTEntries;
+
+ /** Number of samples in the sampling phase. */
+ const unsigned numSamples;
+
+ /** Number of samples taken so far. */
+ unsigned takenSamples;
+
+ /**
+ * The phase that the compressor is at. It assumes that sampling and
+ * code generation are done only once.
+ */
+ enum Phase {SAMPLING, CODE_GENERATION, COMPRESSING};
+ Phase phase;
+
+ class VFTEntry : public TaggedEntry
+ {
+ public:
+ /**
+ * The value is stored as a 64 bit entry to accomodate for the
+ * uncompressed value. All real values must be 32 bits.
+ */
+ uint64_t value;
+
+ /**
+ * The ideal counter width (in bits) is determined by the maximum
+ * number of times a given value appears in the cache
+ * (log2(cache_size / chunkSizeBits)). If smaller counters are used
+ * their values should be rescaled when saturated.
+ */
+ SatCounter32 counter;
+
+ VFTEntry(std::size_t num_bits)
+ : TaggedEntry(), value(0), counter(num_bits)
+ {
+ }
+
+ void
+ invalidate() override
+ {
+ TaggedEntry::invalidate();
+ value = 0;
+ counter.reset();
+ }
+ };
+
+ /**
+ * The Value Frequency Table, a small cache that keeps track and estimates
+ * the frequency distribution of values in the cache.
+ */
+ AssociativeSet<VFTEntry> VFT;
+
+ /**
+ * A pseudo value is used as the representation of uncompressed values.
+ * This value is a random value that is not present in the VFT. It is
+ * selected at the end of the sampling phase.
+ */
+ uint64_t uncompressedValue;
+
+ /** Event to handle finishing code generation and starting compression. */
+ EventFunctionWrapper codeGenerationEvent;
+
+ /**
+ * Sample values from a packet, adding them to the VFT.
+ *
+ * @param data The line being sampled.
+ * @param is_invalidation whether this event comes from an invalidation.
+ */
+ void sampleValues(const std::vector<uint64_t> &data,
+ bool is_invalidation);
+
+ /** End sampling phase and start the code generation. */
+ void generateCodes();
+
+ std::unique_ptr<Base::CompressionData> compress(
+ const std::vector<Chunk>& chunks, Cycles& comp_lat,
+ Cycles& decomp_lat) override;
+
+ void decompress(const CompressionData* comp_data, uint64_t* data) override;
+
+ public:
+ typedef FrequentValuesCompressorParams Params;
+ FrequentValues(const Params &p);
+ ~FrequentValues() = default;
+
+ /**
+ * Process a notification event from the ProbeListener.
+ *
+ * @param data_update The data regarding the entry's contents update.
+ */
+ void probeNotify(const DataUpdate &data_update);
+
+ void regProbeListeners() override;
+};
+
+class FrequentValues::CompData : public CompressionData
+{
+ public:
+ /**
+ * A compressed value contains its encoding, and the compressed data
+ * itself.
+ */
+ struct CompressedValue
+ {
+ /** The codeword.*/
+ Encoder::Code code;
+
+ /**
+ * Original value, stored both for when the codeword marks an
+ * uncompressed entry, and to verify correctness.
+ */
+ uint64_t value;
+
+ CompressedValue(Encoder::Code _code, uint64_t _value)
+ : code(_code), value(_value)
+ {
+ }
+ };
+
+ /**
+ * The values contained in the original data, after being compressed
+ * sequentially.
+ */
+ std::vector<CompressedValue> compressedValues;
+};
+
+} // namespace Compressor
+
+#endif //__MEM_CACHE_COMPRESSORS_FREQUENT_VALUES_HH__
}
}
+void
+Multi::setCache(BaseCache *_cache)
+{
+ Base::setCache(_cache);
+ for (auto& compressor : compressors) {
+ compressor->setCache(_cache);
+ }
+}
+
std::unique_ptr<Base::CompressionData>
Multi::compress(const std::vector<Chunk>& chunks, Cycles& comp_lat,
Cycles& decomp_lat)
Multi(const Params &p);
~Multi();
+ void setCache(BaseCache *_cache) override;
+
std::unique_ptr<Base::CompressionData> compress(
const std::vector<Base::Chunk>& chunks,
Cycles& comp_lat, Cycles& decomp_lat) override;