misc: Standardize the way create() constructs SimObjects.
[gem5.git] / src / mem / cache / compressors / base.cc
1 /*
2 * Copyright (c) 2018-2020 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /** @file
30 * Definition of a basic cache compressor.
31 */
32
33 #include "mem/cache/compressors/base.hh"
34
35 #include <algorithm>
36 #include <climits>
37 #include <cmath>
38 #include <cstdint>
39 #include <string>
40
41 #include "base/trace.hh"
42 #include "debug/CacheComp.hh"
43 #include "mem/cache/tags/super_blk.hh"
44 #include "params/BaseCacheCompressor.hh"
45
46 namespace Compressor {
47
48 // Uncomment this line if debugging compression
49 //#define DEBUG_COMPRESSION
50
51 Base::CompressionData::CompressionData()
52 : _size(0)
53 {
54 }
55
56 Base::CompressionData::~CompressionData()
57 {
58 }
59
60 void
61 Base::CompressionData::setSizeBits(std::size_t size)
62 {
63 _size = size;
64 }
65
66 std::size_t
67 Base::CompressionData::getSizeBits() const
68 {
69 return _size;
70 }
71
72 std::size_t
73 Base::CompressionData::getSize() const
74 {
75 return std::ceil(_size/8);
76 }
77
78 Base::Base(const Params &p)
79 : SimObject(p), blkSize(p.block_size), chunkSizeBits(p.chunk_size_bits),
80 sizeThreshold((blkSize * p.size_threshold_percentage) / 100),
81 stats(*this)
82 {
83 fatal_if(64 % chunkSizeBits,
84 "64 must be a multiple of the chunk granularity.");
85
86 fatal_if(blkSize < sizeThreshold, "Compressed data must fit in a block");
87 }
88
89 std::vector<Base::Chunk>
90 Base::toChunks(const uint64_t* data) const
91 {
92 // Number of chunks in a 64-bit value
93 const unsigned num_chunks_per_64 =
94 (sizeof(uint64_t) * CHAR_BIT) / chunkSizeBits;
95
96 // Turn a 64-bit array into a chunkSizeBits-array
97 std::vector<Chunk> chunks((blkSize * CHAR_BIT) / chunkSizeBits, 0);
98 for (int i = 0; i < chunks.size(); i++) {
99 const int index_64 = std::floor(i / (double)num_chunks_per_64);
100 const unsigned start = i % num_chunks_per_64;
101 chunks[i] = bits(data[index_64],
102 (start + 1) * chunkSizeBits - 1, start * chunkSizeBits);
103 }
104
105 return chunks;
106 }
107
108 void
109 Base::fromChunks(const std::vector<Chunk>& chunks, uint64_t* data) const
110 {
111 // Number of chunks in a 64-bit value
112 const unsigned num_chunks_per_64 =
113 (sizeof(uint64_t) * CHAR_BIT) / chunkSizeBits;
114
115 // Turn a chunkSizeBits-array into a 64-bit array
116 std::memset(data, 0, blkSize);
117 for (int i = 0; i < chunks.size(); i++) {
118 const int index_64 = std::floor(i / (double)num_chunks_per_64);
119 const unsigned start = i % num_chunks_per_64;
120 replaceBits(data[index_64], (start + 1) * chunkSizeBits - 1,
121 start * chunkSizeBits, chunks[i]);
122 }
123 }
124
125 std::unique_ptr<Base::CompressionData>
126 Base::compress(const uint64_t* data, Cycles& comp_lat, Cycles& decomp_lat)
127 {
128 // Apply compression
129 std::unique_ptr<CompressionData> comp_data =
130 compress(toChunks(data), comp_lat, decomp_lat);
131
132 // If we are in debug mode apply decompression just after the compression.
133 // If the results do not match, we've got an error
134 #ifdef DEBUG_COMPRESSION
135 uint64_t decomp_data[blkSize/8];
136
137 // Apply decompression
138 decompress(comp_data.get(), decomp_data);
139
140 // Check if decompressed line matches original cache line
141 fatal_if(std::memcmp(data, decomp_data, blkSize),
142 "Decompressed line does not match original line.");
143 #endif
144
145 // Get compression size. If compressed size is greater than the size
146 // threshold, the compression is seen as unsuccessful
147 std::size_t comp_size_bits = comp_data->getSizeBits();
148 if (comp_size_bits > sizeThreshold * CHAR_BIT) {
149 comp_size_bits = blkSize * CHAR_BIT;
150 comp_data->setSizeBits(comp_size_bits);
151 stats.failedCompressions++;
152 }
153
154 // Update stats
155 stats.compressions++;
156 stats.compressionSizeBits += comp_size_bits;
157 if (comp_size_bits != 0) {
158 stats.compressionSize[1 + std::ceil(std::log2(comp_size_bits))]++;
159 } else {
160 stats.compressionSize[0]++;
161 }
162
163 // Print debug information
164 DPRINTF(CacheComp, "Compressed cache line from %d to %d bits. " \
165 "Compression latency: %llu, decompression latency: %llu\n",
166 blkSize*8, comp_size_bits, comp_lat, decomp_lat);
167
168 return comp_data;
169 }
170
171 Cycles
172 Base::getDecompressionLatency(const CacheBlk* blk)
173 {
174 const CompressionBlk* comp_blk = static_cast<const CompressionBlk*>(blk);
175
176 // If block is compressed, return its decompression latency
177 if (comp_blk && comp_blk->isCompressed()){
178 const Cycles decomp_lat = comp_blk->getDecompressionLatency();
179 DPRINTF(CacheComp, "Decompressing block: %s (%d cycles)\n",
180 comp_blk->print(), decomp_lat);
181 stats.decompressions += 1;
182 return decomp_lat;
183 }
184
185 // Block is not compressed, so there is no decompression latency
186 return Cycles(0);
187 }
188
189 void
190 Base::setDecompressionLatency(CacheBlk* blk, const Cycles lat)
191 {
192 // Sanity check
193 assert(blk != nullptr);
194
195 // Assign latency
196 static_cast<CompressionBlk*>(blk)->setDecompressionLatency(lat);
197 }
198
199 void
200 Base::setSizeBits(CacheBlk* blk, const std::size_t size_bits)
201 {
202 // Sanity check
203 assert(blk != nullptr);
204
205 // Assign size
206 static_cast<CompressionBlk*>(blk)->setSizeBits(size_bits);
207 }
208
209 Base::BaseStats::BaseStats(Base& _compressor)
210 : Stats::Group(&_compressor), compressor(_compressor),
211 compressions(this, "compressions",
212 "Total number of compressions"),
213 failedCompressions(this, "failed_compressions",
214 "Total number of failed compressions"),
215 compressionSize(this, "compression_size",
216 "Number of blocks that were compressed to this power of two size"),
217 compressionSizeBits(this, "compression_size_bits",
218 "Total compressed data size, in bits"),
219 avgCompressionSizeBits(this, "avg_compression_size_bits",
220 "Average compression size, in bits"),
221 decompressions(this, "total_decompressions",
222 "Total number of decompressions")
223 {
224 }
225
226 void
227 Base::BaseStats::regStats()
228 {
229 Stats::Group::regStats();
230
231 // Values comprised are {0, 1, 2, 4, ..., blkSize}
232 compressionSize.init(std::log2(compressor.blkSize*8) + 2);
233 compressionSize.subname(0, "0");
234 compressionSize.subdesc(0,
235 "Number of blocks that compressed to fit in 0 bits");
236 for (unsigned i = 0; i <= std::log2(compressor.blkSize*8); ++i) {
237 std::string str_i = std::to_string(1 << i);
238 compressionSize.subname(1+i, str_i);
239 compressionSize.subdesc(1+i,
240 "Number of blocks that compressed to fit in " + str_i + " bits");
241 }
242
243 avgCompressionSizeBits.flags(Stats::total | Stats::nozero | Stats::nonan);
244 avgCompressionSizeBits = compressionSizeBits / compressions;
245 }
246
247 } // namespace Compressor