| /** |
| * Copyright (c) 2019, 2020 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. |
| */ |
| |
| #include "mem/cache/replacement_policies/dueling_rp.hh" |
| |
| #include "base/logging.hh" |
| #include "params/DuelingRP.hh" |
| |
| namespace gem5 |
| { |
| |
| namespace replacement_policy |
| { |
| |
| Dueling::Dueling(const Params &p) |
| : Base(p), replPolicyA(p.replacement_policy_a), |
| replPolicyB(p.replacement_policy_b), |
| duelingMonitor(p.constituency_size, p.team_size), |
| duelingStats(this) |
| { |
| fatal_if((replPolicyA == nullptr) || (replPolicyB == nullptr), |
| "All replacement policies must be instantiated"); |
| } |
| |
| void |
| Dueling::invalidate(const std::shared_ptr<ReplacementData>& replacement_data) |
| { |
| std::shared_ptr<DuelerReplData> casted_replacement_data = |
| std::static_pointer_cast<DuelerReplData>(replacement_data); |
| replPolicyA->invalidate(casted_replacement_data->replDataA); |
| replPolicyB->invalidate(casted_replacement_data->replDataB); |
| } |
| |
| void |
| Dueling::touch(const std::shared_ptr<ReplacementData>& replacement_data, |
| const PacketPtr pkt) |
| { |
| std::shared_ptr<DuelerReplData> casted_replacement_data = |
| std::static_pointer_cast<DuelerReplData>(replacement_data); |
| replPolicyA->touch(casted_replacement_data->replDataA, pkt); |
| replPolicyB->touch(casted_replacement_data->replDataB, pkt); |
| } |
| |
| void |
| Dueling::touch(const std::shared_ptr<ReplacementData>& replacement_data) const |
| { |
| std::shared_ptr<DuelerReplData> casted_replacement_data = |
| std::static_pointer_cast<DuelerReplData>(replacement_data); |
| replPolicyA->touch(casted_replacement_data->replDataA); |
| replPolicyB->touch(casted_replacement_data->replDataB); |
| } |
| |
| void |
| Dueling::reset(const std::shared_ptr<ReplacementData>& replacement_data, |
| const PacketPtr pkt) |
| { |
| std::shared_ptr<DuelerReplData> casted_replacement_data = |
| std::static_pointer_cast<DuelerReplData>(replacement_data); |
| replPolicyA->reset(casted_replacement_data->replDataA, pkt); |
| replPolicyB->reset(casted_replacement_data->replDataB, pkt); |
| |
| // A miss in a set is a sample to the duel. A call to this function |
| // implies in the replacement of an entry, which was either caused by |
| // a miss, an external invalidation, or the initialization of the table |
| // entry (when warming up) |
| duelingMonitor.sample(static_cast<Dueler*>(casted_replacement_data.get())); |
| } |
| |
| void |
| Dueling::reset(const std::shared_ptr<ReplacementData>& replacement_data) const |
| { |
| std::shared_ptr<DuelerReplData> casted_replacement_data = |
| std::static_pointer_cast<DuelerReplData>(replacement_data); |
| replPolicyA->reset(casted_replacement_data->replDataA); |
| replPolicyB->reset(casted_replacement_data->replDataB); |
| |
| // A miss in a set is a sample to the duel. A call to this function |
| // implies in the replacement of an entry, which was either caused by |
| // a miss, an external invalidation, or the initialization of the table |
| // entry (when warming up) |
| duelingMonitor.sample(static_cast<Dueler*>(casted_replacement_data.get())); |
| } |
| |
| ReplaceableEntry* |
| Dueling::getVictim(const ReplacementCandidates& candidates) const |
| { |
| // This function assumes that all candidates are either part of the same |
| // sampled set, or are not samples. |
| // @todo This should be improved at some point. |
| panic_if(candidates.size() != params().team_size, "We currently only " |
| "support team sizes that match the number of replacement candidates"); |
| |
| // The team with the most misses loses |
| bool winner = !duelingMonitor.getWinner(); |
| |
| // If the entry is a sample, it can only be used with a certain policy. |
| bool team; |
| bool is_sample = duelingMonitor.isSample(static_cast<Dueler*>( |
| std::static_pointer_cast<DuelerReplData>( |
| candidates[0]->replacementData).get()), team); |
| |
| // All replacement candidates must be set appropriately, so that the |
| // proper replacement data is used. A replacement policy X must be used |
| // if the candidates are its samples - in which case they must always |
| // use X - or if it is not a sample, and X is currently the best RP. |
| // This assumes that A's team is "false", and B's team is "true". |
| bool team_a; |
| if ((is_sample && !team) || (!is_sample && !winner)) { |
| duelingStats.selectedA++; |
| team_a = true; |
| } else { |
| duelingStats.selectedB++; |
| team_a = false; |
| } |
| |
| // Create a temporary list of replacement candidates which re-routes the |
| // replacement data of the selected team |
| std::vector<std::shared_ptr<ReplacementData>> dueling_replacement_data; |
| for (auto& candidate : candidates) { |
| std::shared_ptr<DuelerReplData> dueler_repl_data = |
| std::static_pointer_cast<DuelerReplData>( |
| candidate->replacementData); |
| |
| // As of now we assume that all candidates are either part of |
| // the same sampled team, or are not samples. |
| bool candidate_team; |
| panic_if( |
| duelingMonitor.isSample(dueler_repl_data.get(), candidate_team) && |
| (team != candidate_team), |
| "Not all sampled candidates belong to the same team"); |
| |
| // Copy the original entry's data, re-routing its replacement data |
| // to the selected one |
| dueling_replacement_data.push_back(dueler_repl_data); |
| candidate->replacementData = team_a ? dueler_repl_data->replDataA : |
| dueler_repl_data->replDataB; |
| } |
| |
| // Use the selected replacement policy to find the victim |
| ReplaceableEntry* victim = team_a ? replPolicyA->getVictim(candidates) : |
| replPolicyB->getVictim(candidates); |
| |
| // Search for entry within the original candidates and clean-up duplicates |
| for (int i = 0; i < candidates.size(); i++) { |
| candidates[i]->replacementData = dueling_replacement_data[i]; |
| } |
| |
| return victim; |
| } |
| |
| std::shared_ptr<ReplacementData> |
| Dueling::instantiateEntry() |
| { |
| DuelerReplData* replacement_data = new DuelerReplData( |
| replPolicyA->instantiateEntry(), replPolicyB->instantiateEntry()); |
| duelingMonitor.initEntry(static_cast<Dueler*>(replacement_data)); |
| return std::shared_ptr<DuelerReplData>(replacement_data); |
| } |
| |
| Dueling::DuelingStats::DuelingStats(statistics::Group* parent) |
| : statistics::Group(parent), |
| ADD_STAT(selectedA, "Number of times A was selected to victimize"), |
| ADD_STAT(selectedB, "Number of times B was selected to victimize") |
| { |
| } |
| |
| } // namespace replacement_policy |
| } // namespace gem5 |