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