1/** 2 * Copyright (c) 2018 Metempsy Technology Consulting 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 * Authors: Javier Bueno 29 */ 30 31#include "mem/cache/prefetch/access_map_pattern_matching.hh" 32 33#include "debug/HWPrefetch.hh" 34#include "mem/cache/prefetch/associative_set_impl.hh" 35#include "params/AMPMPrefetcher.hh" 36#include "params/AccessMapPatternMatching.hh" 37 38AccessMapPatternMatching::AccessMapPatternMatching( 39 const AccessMapPatternMatchingParams *p) 40 : ClockedObject(p), blkSize(p->block_size), limitStride(p->limit_stride), 41 startDegree(p->start_degree), hotZoneSize(p->hot_zone_size), 42 highCoverageThreshold(p->high_coverage_threshold), 43 lowCoverageThreshold(p->low_coverage_threshold), 44 highAccuracyThreshold(p->high_accuracy_threshold), 45 lowAccuracyThreshold(p->low_accuracy_threshold), 46 highCacheHitThreshold(p->high_cache_hit_threshold), 47 lowCacheHitThreshold(p->low_cache_hit_threshold), 48 epochCycles(p->epoch_cycles), 49 offChipMemoryLatency(p->offchip_memory_latency), 50 accessMapTable(p->access_map_table_assoc, p->access_map_table_entries, 51 p->access_map_table_indexing_policy, 52 p->access_map_table_replacement_policy, 53 AccessMapEntry(hotZoneSize / blkSize)), 54 numGoodPrefetches(0), numTotalPrefetches(0), numRawCacheMisses(0), 55 numRawCacheHits(0), degree(startDegree), usefulDegree(startDegree), 56 epochEvent([this]{ processEpochEvent(); }, name()) 57{ 58 fatal_if(!isPowerOf2(hotZoneSize), 59 "the hot zone size must be a power of 2"); 60} 61 62void 63AccessMapPatternMatching::startup() 64{ 65 schedule(epochEvent, clockEdge(epochCycles)); 66} 67 68void 69AccessMapPatternMatching::processEpochEvent() 70{ 71 schedule(epochEvent, clockEdge(epochCycles)); 72 double prefetch_accuracy = 73 ((double) numGoodPrefetches) / ((double) numTotalPrefetches); 74 double prefetch_coverage = 75 ((double) numGoodPrefetches) / ((double) numRawCacheMisses); 76 double cache_hit_ratio = ((double) numRawCacheHits) / 77 ((double) (numRawCacheHits + numRawCacheMisses)); 78 double num_requests = (double) (numRawCacheMisses - numGoodPrefetches + 79 numTotalPrefetches); 80 double memory_bandwidth = num_requests * offChipMemoryLatency / 81 clockEdge(epochCycles); 82 83 if (prefetch_coverage > highCoverageThreshold && 84 (prefetch_accuracy > highAccuracyThreshold || 85 cache_hit_ratio < lowCacheHitThreshold)) { 86 usefulDegree += 1; 87 } else if ((prefetch_coverage < lowCoverageThreshold && 88 (prefetch_accuracy < lowAccuracyThreshold || 89 cache_hit_ratio > highCacheHitThreshold)) || 90 (prefetch_accuracy < lowAccuracyThreshold && 91 cache_hit_ratio > highCacheHitThreshold)) { 92 usefulDegree -= 1; 93 } 94 degree = std::min((unsigned) memory_bandwidth, usefulDegree); 95 // reset epoch stats 96 numGoodPrefetches = 0.0; 97 numTotalPrefetches = 0.0; 98 numRawCacheMisses = 0.0; 99 numRawCacheHits = 0.0; 100} 101 102AccessMapPatternMatching::AccessMapEntry * 103AccessMapPatternMatching::getAccessMapEntry(Addr am_addr, 104 bool is_secure) 105{ 106 AccessMapEntry *am_entry = accessMapTable.findEntry(am_addr, is_secure); 107 if (am_entry != nullptr) { 108 accessMapTable.accessEntry(am_entry); 109 } else { 110 am_entry = accessMapTable.findVictim(am_addr); 111 assert(am_entry != nullptr); 112 113 accessMapTable.insertEntry(am_addr, is_secure, am_entry); 114 } 115 return am_entry; 116} 117 118void 119AccessMapPatternMatching::setEntryState(AccessMapEntry &entry, 120 Addr block, enum AccessMapState state) 121{ 122 enum AccessMapState old = entry.states[block]; 123 entry.states[block] = state; 124 125 //do not update stats when initializing 126 if (state == AM_INIT) return; 127 128 switch (old) { 129 case AM_INIT: 130 if (state == AM_PREFETCH) { 131 numTotalPrefetches += 1; 132 } else if (state == AM_ACCESS) { 133 numRawCacheMisses += 1; 134 } 135 break; 136 case AM_PREFETCH: 137 if (state == AM_ACCESS) { 138 numGoodPrefetches += 1; 139 numRawCacheMisses += 1; 140 } 141 break; 142 case AM_ACCESS: 143 if (state == AM_ACCESS) { 144 numRawCacheHits += 1; 145 } 146 break; 147 default: 148 panic("Impossible path\n"); 149 break; 150 } 151} 152 153void 154AccessMapPatternMatching::calculatePrefetch( 155 const BasePrefetcher::PrefetchInfo &pfi, 156 std::vector<QueuedPrefetcher::AddrPriority> &addresses) 157{ 158 assert(addresses.empty()); 159 160 bool is_secure = pfi.isSecure(); 161 Addr am_addr = pfi.getAddr() / hotZoneSize; 162 Addr current_block = (pfi.getAddr() % hotZoneSize) / blkSize; 163 uint64_t lines_per_zone = hotZoneSize / blkSize; 164 165 // Get the entries of the curent block (am_addr), the previous, and the 166 // following ones 167 AccessMapEntry *am_entry_curr = getAccessMapEntry(am_addr, is_secure); 168 AccessMapEntry *am_entry_prev = (am_addr > 0) ? 169 getAccessMapEntry(am_addr-1, is_secure) : nullptr; 170 AccessMapEntry *am_entry_next = (am_addr < (MaxAddr/hotZoneSize)) ? 171 getAccessMapEntry(am_addr+1, is_secure) : nullptr; 172 assert(am_entry_curr != am_entry_prev); 173 assert(am_entry_curr != am_entry_next); 174 assert(am_entry_prev != am_entry_next); 175 assert(am_entry_curr != nullptr); 176 177 //Mark the current access as Accessed 178 setEntryState(*am_entry_curr, current_block, AM_ACCESS); 179 180 /** 181 * Create a contiguous copy of the 3 entries states. 182 * With this, we avoid doing boundaries checking in the loop that looks 183 * for prefetch candidates, mark out of range positions with AM_INVALID 184 */ 185 std::vector<AccessMapState> states(3 * lines_per_zone); 186 for (unsigned idx = 0; idx < lines_per_zone; idx += 1) { 187 states[idx] = 188 am_entry_prev != nullptr ? am_entry_prev->states[idx] : AM_INVALID; 189 states[idx + lines_per_zone] = am_entry_curr->states[idx]; 190 states[idx + 2 * lines_per_zone] = 191 am_entry_next != nullptr ? am_entry_next->states[idx] : AM_INVALID; 192 } 193 194 /** 195 * am_entry_prev->states => states[ 0 .. lines_per_zone-1] 196 * am_entry_curr->states => states[ lines_per_zone .. 2*lines_per_zone-1] 197 * am_entry_next->states => states[2*lines_per_zone .. 3*lines_per_zone-1] 198 */ 199 200 // index of the current_block in the new vector 201 Addr states_current_block = current_block + lines_per_zone; 202 // consider strides 1..lines_per_zone/2 203 int max_stride = limitStride == 0 ? lines_per_zone / 2 : limitStride + 1; 204 for (int stride = 1; stride < max_stride; stride += 1) { 205 // Test accessed positive strides 206 if (checkCandidate(states, states_current_block, stride)) { 207 // candidate found, current_block - stride 208 Addr pf_addr; 209 if (stride > current_block) { 210 // The index (current_block - stride) falls in the range of 211 // the previous zone (am_entry_prev), adjust the address 212 // accordingly 213 Addr blk = states_current_block - stride; 214 pf_addr = (am_addr - 1) * hotZoneSize + blk * blkSize; 215 setEntryState(*am_entry_prev, blk, AM_PREFETCH); 216 } else { 217 // The index (current_block - stride) falls within 218 // am_entry_curr 219 Addr blk = current_block - stride; 220 pf_addr = am_addr * hotZoneSize + blk * blkSize; 221 setEntryState(*am_entry_curr, blk, AM_PREFETCH); 222 } 223 addresses.push_back(QueuedPrefetcher::AddrPriority(pf_addr, 0)); 224 if (addresses.size() == degree) { 225 break; 226 } 227 } 228 229 // Test accessed negative strides 230 if (checkCandidate(states, states_current_block, -stride)) { 231 // candidate found, current_block + stride 232 Addr pf_addr; 233 if (current_block + stride >= lines_per_zone) { 234 // The index (current_block + stride) falls in the range of 235 // the next zone (am_entry_next), adjust the address 236 // accordingly 237 Addr blk = (states_current_block + stride) % lines_per_zone; 238 pf_addr = (am_addr + 1) * hotZoneSize + blk * blkSize; 239 setEntryState(*am_entry_next, blk, AM_PREFETCH); 240 } else { 241 // The index (current_block + stride) falls within 242 // am_entry_curr 243 Addr blk = current_block + stride; 244 pf_addr = am_addr * hotZoneSize + blk * blkSize; 245 setEntryState(*am_entry_curr, blk, AM_PREFETCH); 246 } 247 addresses.push_back(QueuedPrefetcher::AddrPriority(pf_addr, 0)); 248 if (addresses.size() == degree) { 249 break; 250 } 251 } 252 } 253} 254 255AccessMapPatternMatching* 256AccessMapPatternMatchingParams::create() 257{ 258 return new AccessMapPatternMatching(this); 259} 260 261AMPMPrefetcher::AMPMPrefetcher(const AMPMPrefetcherParams *p) 262 : QueuedPrefetcher(p), ampm(*p->ampm) 263{ 264} 265 266void 267AMPMPrefetcher::calculatePrefetch(const PrefetchInfo &pfi, 268 std::vector<AddrPriority> &addresses) 269{ 270 ampm.calculatePrefetch(pfi, addresses); 271} 272 273AMPMPrefetcher* 274AMPMPrefetcherParams::create() 275{ 276 return new AMPMPrefetcher(this); 277} 278