blob: 1192963e227082cef87a41c20fa14cedaca60554 [file] [log] [blame]
/*
* Copyright (c) 2018 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.
*
* Authors: Daniel Carvalho
*/
/** @file
* Implementation of the CPack cache compressor.
*/
#include "mem/cache/compressors/cpack.hh"
#include <algorithm>
#include <cstdint>
#include "debug/CacheComp.hh"
#include "params/CPack.hh"
CPack::CompData::CompData(const std::size_t dictionary_size)
: CompressionData()
{
}
CPack::CompData::~CompData()
{
}
CPack::CPack(const Params *p)
: BaseCacheCompressor(p), dictionarySize(2*blkSize/8)
{
dictionary.resize(dictionarySize);
resetDictionary();
}
void
CPack::resetDictionary()
{
// Reset number of valid entries
numEntries = 0;
// Set all entries as 0
std::array<uint8_t, 4> zero_word = {0, 0, 0, 0};
std::fill(dictionary.begin(), dictionary.end(), zero_word);
}
std::unique_ptr<CPack::Pattern>
CPack::compressWord(const uint32_t data)
{
// Split data in bytes
const std::array<uint8_t, 4> bytes = {
static_cast<uint8_t>(data & 0xFF),
static_cast<uint8_t>((data >> 8) & 0xFF),
static_cast<uint8_t>((data >> 16) & 0xFF),
static_cast<uint8_t>((data >> 24) & 0xFF)
};
// Start as a no-match pattern. A negative match location is used so that
// patterns that depend on the dictionary entry don't match
std::unique_ptr<Pattern> pattern =
PatternFactory::getPattern(bytes, {0, 0, 0, 0}, -1);
// Search for word on dictionary
for (std::size_t i = 0; i < numEntries; i++) {
// Try matching input with possible patterns
std::unique_ptr<Pattern> temp_pattern =
PatternFactory::getPattern(bytes, dictionary[i], i);
// Check if found pattern is better than previous
if (temp_pattern->getSizeBits() < pattern->getSizeBits()) {
pattern = std::move(temp_pattern);
}
}
// Update stats
patternStats[pattern->getPatternNumber()]++;
// Push into dictionary
if ((numEntries < dictionarySize) && pattern->shouldAllocate()) {
dictionary[numEntries++] = bytes;
}
return pattern;
}
std::unique_ptr<BaseCacheCompressor::CompressionData>
CPack::compress(const uint64_t* data, Cycles& comp_lat, Cycles& decomp_lat)
{
std::unique_ptr<CompData> comp_data =
std::unique_ptr<CompData>(new CompData(dictionarySize));
// Compression size
std::size_t size = 0;
// Reset dictionary
resetDictionary();
// Compress every word sequentially
for (std::size_t i = 0; i < blkSize/8; i++) {
const uint32_t first_word = ((data[i])&0xFFFFFFFF00000000) >> 32;
const uint32_t second_word = (data[i])&0x00000000FFFFFFFF;
// Compress both words
std::unique_ptr<Pattern> first_pattern = compressWord(first_word);
std::unique_ptr<Pattern> second_pattern = compressWord(second_word);
// Update total line compression size
size += first_pattern->getSizeBits() + second_pattern->getSizeBits();
// Print debug information
DPRINTF(CacheComp, "Compressed %08x to %s\n", first_word,
first_pattern->print());
DPRINTF(CacheComp, "Compressed %08x to %s\n", second_word,
second_pattern->print());
// Append to pattern list
comp_data->entries.push_back(std::move(first_pattern));
comp_data->entries.push_back(std::move(second_pattern));
}
// Set final compression size
comp_data->setSizeBits(size);
// Set compression latency (Accounts for pattern matching, length
// generation, packaging and shifting)
comp_lat = Cycles(blkSize/8+5);
// Set decompression latency (1 qword per cycle)
decomp_lat = Cycles(blkSize/8);
// Return compressed line
return std::move(comp_data);
}
uint32_t
CPack::decompressWord(const Pattern* pattern)
{
std::array<uint8_t, 4> data;
// Search for matching entry
std::vector<std::array<uint8_t, 4>>::iterator entry_it =
dictionary.begin();
std::advance(entry_it, pattern->getMatchLocation());
// Decompress the match. If the decompressed value must be added to
// the dictionary, do it
if (pattern->decompress(*entry_it, data)) {
dictionary[numEntries++] = data;
}
// Return word
return (((((data[3] << 8) | data[2]) << 8) | data[1]) << 8) | data[0];
}
void
CPack::decompress(const CompressionData* comp_data, uint64_t* data)
{
const CompData* cpack_comp_data = static_cast<const CompData*>(comp_data);
// Reset dictionary
resetDictionary();
// Decompress every entry sequentially
std::vector<uint32_t> decomp_words;
for (const auto& entry : cpack_comp_data->entries) {
const uint32_t word = decompressWord(&*entry);
decomp_words.push_back(word);
// Print debug information
DPRINTF(CacheComp, "Decompressed %s to %x\n", entry->print(), word);
}
// Concatenate the decompressed words to generate the cache lines
for (std::size_t i = 0; i < blkSize/8; i++) {
data[i] = (static_cast<uint64_t>(decomp_words[2*i]) << 32) |
decomp_words[2*i+1];
}
}
void
CPack::regStats()
{
BaseCacheCompressor::regStats();
// We store the frequency of each pattern
patternStats
.init(Pattern::getNumPatterns())
.name(name() + ".pattern")
.desc("Number of data entries that were compressed to this pattern.")
;
for (unsigned i = 0; i < Pattern::getNumPatterns(); ++i) {
patternStats.subname(i, Pattern::getName(i));
patternStats.subdesc(i, "Number of data entries that match pattern " +
Pattern::getName(i));
}
}
CPack*
CPackParams::create()
{
return new CPack(this);
}