/* * 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 #include #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 zero_word = {0, 0, 0, 0}; std::fill(dictionary.begin(), dictionary.end(), zero_word); } std::unique_ptr CPack::compressWord(const uint32_t data) { // Split data in bytes const std::array bytes = { static_cast(data & 0xFF), static_cast((data >> 8) & 0xFF), static_cast((data >> 16) & 0xFF), static_cast((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 = 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 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 CPack::compress(const uint64_t* data, Cycles& comp_lat, Cycles& decomp_lat) { std::unique_ptr comp_data = std::unique_ptr(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 first_pattern = compressWord(first_word); std::unique_ptr 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 data; // Search for matching entry std::vector>::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(comp_data); // Reset dictionary resetDictionary(); // Decompress every entry sequentially std::vector 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(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); }