From d9cfc5dbe1da40d406a9ed701ac815d6a5dd3ffb Mon Sep 17 00:00:00 2001 From: Maxwell Bradley Date: Wed, 29 Jul 2020 17:30:02 +0000 Subject: [PATCH 1/5] Added best offset prefetcher source files to forked branch. --- src/SConscript | 1 + src/best_offset_prefetcher.cpp | 269 +++++++++++++++++++++++++++++++++ src/best_offset_prefetcher.h | 121 +++++++++++++++ src/init.cpp | 22 +++ src/trace_init.cpp | 22 +++ src/trace_reader_memtrace.cpp | 2 +- 6 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 src/best_offset_prefetcher.cpp create mode 100644 src/best_offset_prefetcher.h diff --git a/src/SConscript b/src/SConscript index 28593a31c..2bf2e4bba 100644 --- a/src/SConscript +++ b/src/SConscript @@ -28,6 +28,7 @@ traceSrcs = [ "monitor.cpp", "network.cpp", "next_line_prefetcher.cpp", + "best_offset_prefetcher.cpp", "null_core.cpp", "ooo_core.cpp", "ooo_core_recorder.cpp", diff --git a/src/best_offset_prefetcher.cpp b/src/best_offset_prefetcher.cpp new file mode 100644 index 000000000..8565960f8 --- /dev/null +++ b/src/best_offset_prefetcher.cpp @@ -0,0 +1,269 @@ +/** $lic$ + * This file is part of zsim. + * + * zsim is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, version 2. + * + * If you use this software in your research, we request that you reference + * the zsim paper ("ZSim: Fast and Accurate Microarchitectural Simulation of + * Thousand-Core Systems", Sanchez and Kozyrakis, ISCA-40, June 2013) as the + * source of the simulator in any publications that use this software, and that + * you send us a citation of your work. + * + * zsim is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "best_offset_prefetcher.h" +#define L3_ACCESS_TIME 49 +//#define L3_ACCESS_TIME 27 +//#define L3_ACCESS_TIME 30 + +// helper to reset offset scores to 0 +void BestOffsetPrefetcher::reset_offsets(){ + for (auto& i : offset_scores){ + i.second = 0; + } +} +void BestOffsetPrefetcher::print_scores(){ + std::cout << std::endl; + std::cout << std::endl; + for (auto& i : offset_scores){ + if (i.second != 0) + std::cout << "Offset: " << i.first << " Score: " << i.second << std::endl; + } +} + +void BestOffsetPrefetcher::reset_prefetcher(){ + // return all offsets to 0 + reset_offsets(); + // set current round back to 0 + this->current_round = 0; + // set the test offset back to the end index (for timely prefetches) + this->test_offset_index = 0; +} + +uint64_t BestOffsetPrefetcher::find_max_score(){ + std::pair max_score = std::pair(1, BAD_SCORE); + for (auto s : offset_scores){ + if (s.second >= max_score.second){ + // set the offset to the offset value + max_score.first = s.first; + // set the second to the score + max_score.second = s.second; + } + } + return max_score.first; +} + +void BestOffsetPrefetcher::move_test_offset_ptr(){ + // if the current offset is the total minus 1 + if (this->test_offset_index == (NUM_OFFSETS - 1) ){ + // reset the pointer and set the round up + this->test_offset_index = 0; + this->current_round++; + // if max out the nuber of rounds, + // reset the prefetcher + if (this->current_round == ( RND_MAX + 1 ) ){ + this->current_offset = find_max_score(); + reset_prefetcher(); + } + } + else{ + // move address in test_offset_point along + this->test_offset_index++; + } +} + + +void BestOffsetPrefetcher::learn(uint64_t addr, uint64_t cycle){ + // offset to currently test + uint64_t test_offset = (this->offset_scores[this->test_offset_index]).first; + // if we're going off the page, dont' get it + uint64_t base_addr = addr - test_offset; + // if we're going off the page, dont' get it + if ( ((addr - test_offset) < 0 ) + or + ( + // if trying to check on another page, don't do it + (addr & PAGE_MASK) != ((base_addr) & PAGE_MASK) + )){ + // move this offset along + move_test_offset_ptr(); + return; + } + + // check if address in the recent requests table + if (this->recent_requests.exists(base_addr, cycle) == true){ + // if increasing that index by 1 would put us over the max score + // reset the prefetcher + if (this->offset_scores[this->test_offset_index].second == (MAX_SCORE-1)){ + print_scores(); + // increase the score for that offset + this->current_offset = this->offset_scores[this->test_offset_index].first; + reset_prefetcher(); + return; + } + // otherwise, if found the address but not maxed out, increment + // its score + else{ + this->offset_scores[this->test_offset_index].second++; + } + } + move_test_offset_ptr(); +} + +BestOffsetPrefetcher::BestOffsetPrefetcher(const g_string& _name, + const g_string& _target, + bool _monitor_GETS, bool _monitor_GETX, + uint32_t _degree): + CachePrefetcher(_name, _target), monitor_GETS_(_monitor_GETS), + monitor_GETX_(_monitor_GETX), degree_(_degree) { + // init recent requests hash table + this->recent_requests = RR(); + this->offset_scores = std::vector>(); + // load up the offset scores table with the offset and score + for (uint64_t i = 1; i <= NUM_OFFSETS; ++i){ + offset_scores.push_back(std::pair(i, 0)); + } + // set the current round to 0 + this->current_round = 0; + // set the first test offset to the first index in the offset list + this->test_offset_index = 0; + // set the prefetch offset to its init value (defined in header file) + this->current_offset = INIT_OFFSET; +} + + // create the stats for the prefetcher with clean data + +void BestOffsetPrefetcher::initStats(AggregateStat* _parentStat) { + AggregateStat* s = new AggregateStat(); + s->init(name_.c_str(), "Best Offset prefetcher stats"); + prof_emitted_prefetches_.init("pf", "Emitted prefetches"); + s->append(&prof_emitted_prefetches_); + _parentStat->append(s); +} +// note that this function returns the data to whatever caller it wants, +// while prefetch gets the next line + +uint64_t BestOffsetPrefetcher::access(MemReq& _req) { + MemReq req = _req; + req.childId = childId_; + // create a hash for which cache bank to access + uint32_t bank = CacheBankHash::hash(req.lineAddr, parents_.size()); + // if a speculative request, return and don't prefetch + uint64_t resp_cycle = parents_[bank]->access(req); + + if (_req.flags & MemReq::SPECULATIVE) { + return resp_cycle; + } + //Only do data prefetches for now + //if an instruction fetch, don't prefetch + if (req.flags & MemReq::IFETCH) { + return resp_cycle; + } + // if not monitored, don't prefetch either + bool monitored = (monitor_GETS_ && _req.type == GETS) || + (monitor_GETX_ && _req.type == GETX); + if (!monitored) { + return resp_cycle; //Ignore other requests + } + +#ifdef SIMPLE_PC + if (_req.pc == 140577589630834){ +#endif + // if not an instruction fetch, monitored, or a speculative request + // execute a prefetch + // need to figure out if the response cycle is going to DRAM or not + prefetch(_req); + // run the prefetch on the address (better when the first thing you do) + uint64_t time_to_fetch = resp_cycle - req.cycle; + // put the line address and the completion time in the RR + if (time_to_fetch >= L3_ACCESS_TIME){ + this->recent_requests.insert(req.lineAddr, resp_cycle); + // if it's going to DRAM, put it in the RR + // otherwise, put it in the L3 + // run the learning algo with the current cycle in mind + learn(req.lineAddr, req.cycle); + } +#ifdef SIMPLE_PC + } +#endif + return resp_cycle; +} + +// work starts in here +void BestOffsetPrefetcher::prefetch(MemReq& _req) { + auto &queue_info = d_caches_[_req.srcId]; //This is pair + uint64_t prefetch_location = _req.lineAddr + this->current_offset; + if ((prefetch_location & PAGE_MASK) == (_req.lineAddr & PAGE_MASK)){ + queue_info.first->schedulePrefetch(prefetch_location, queue_info.second); + // increment the number of prefetches made + prof_emitted_prefetches_.inc(); + } +} +// this actually needs to check it it's already in the hash map + +void RR::insert(uint64_t addr, uint64_t cycle){ + // to handle deleting + if (this->list.size() == MAX_SIZE){ + this->deletion(); + } + // put the most recent address in the back of the list + this->list.push_back(std::pair(addr, id)); + // see if this address already exists in the map + auto found = this->hash.find(addr); + // if the thing exists in the map already + if (found != this->hash.end()){ + // set the hash id to the last element in the list's id + found->second.first = this->list.back().second; + }else { // if the map doesn't have this already + // put the address of the back of the list in the map + auto new_pair = std::make_pair(addr, std::pair(id++, cycle)); + this->hash.insert(new_pair); + } + this->size++; +} + +bool RR::exists(uint64_t addr, uint64_t cycle){ + auto found = this->hash.find(addr); + if (found != this->hash.end()){ + // make sure the completion cycle for the requet + // is less than the current one + uint64_t completion = found->second.second; + if (completion <= cycle){ + return true; + } + } + return false; +} + +void RR::deletion(){ + // get the front of the list (thing to delete) + std::pair front = list.front(); + // find the corresponding address in the hash map + auto found = hash.find(front.first); + // should always be true + if (found != hash.end()){ + // check if the map entry is pointing to the front + if (found->second.first== list.front().second){ + // if it's pointing to the front, delete it + hash.erase(front.first); + } + // remove the first element from the list + list.pop_front(); + } + this->size--; +} + +RR::RR(){ + this->hash = std::map>(); + this->list = std::deque>(); +} + diff --git a/src/best_offset_prefetcher.h b/src/best_offset_prefetcher.h new file mode 100644 index 000000000..74e434cd9 --- /dev/null +++ b/src/best_offset_prefetcher.h @@ -0,0 +1,121 @@ +/** $lic$ + * +* This file is part of zsim. + * + * zsim is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, version 2. + * + * If you use this software in your research, we request that you reference + * the zsim paper ("ZSim: Fast and Accurate Microarchitectural Simulation of + * Thousand-Core Systems", Sanchez and Kozyrakis, ISCA-40, June 2013) as the + * source of the simulator in any publications that use this software, and that + * you send us a citation of your work. + * + * zsim is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +//#define TEST +//#define SIMPLE_PC + +#ifndef BEST_OFFSET_PREFETCHER_H_ +#define BEST_OFFSET_PREFETCHER_H_ + +#ifndef TEST +#include "cache_prefetcher.h" +#include "filter_cache.h" +#include "g_std/g_string.h" +#include "memory_hierarchy.h" +#include "stats.h" +#include "hash.h" +// hopefully don't need to include this for tests in the .cpp +#else +#include +#include +#include +#include +#include +#endif + +// num run throughs to make before round over +const uint64_t RND_MAX = 100; +//const uint64_t RND_MAX = 50; +// max score before round ended +///const uint64_t MAX_SCORE = 10; +const uint64_t MAX_SCORE = 31; +// score to signal to turn off prefetcher +//const uint64_t BAD_SCORE = 5; +const uint64_t BAD_SCORE = 1; +// offset to start prefetching with +const uint64_t INIT_OFFSET = 1; +const uint64_t NUM_OFFSETS = 63; +const uint64_t MAX_SIZE = RND_MAX * NUM_OFFSETS; + +const uint64_t PAGE_SIZE = pow(2, 12); +const uint64_t PAGE_MASK = 0xFFFFFFFFFFFFFFFF - PAGE_SIZE + 1; +// 64 for normal zsim operation +// rounds to wait until can prefetch again +const uint64_t ROUNDS_TO_WAIT = 2; + +class RR { + public: + void insert(uint64_t, uint64_t); + bool exists(uint64_t, uint64_t); + RR(); + uint64_t size = 0; + private: + std::map> hash; + std::deque> list; + void deletion(); + uint64_t id = 0; +}; + +#ifdef TEST +class BestOffsetPrefetcher { +public: + explicit BestOffsetPrefetcher(); + uint64_t access(uint64_t _req); + void prefetch(uint64_t _req); + uint64_t learn(uint64_t); +#else +class BestOffsetPrefetcher : public CachePrefetcher { +public: + explicit BestOffsetPrefetcher(const g_string& _name, const g_string& _target, + bool _monitor_GETS, bool _monitor_GETX, + uint32_t _degree); + void initStats(AggregateStat* _parentStat) override; + uint64_t access(MemReq& _req) override; + void prefetch(MemReq& _req) override; + void learn(uint64_t,uint64_t); +#endif + + void reset_prefetcher(); +private: + // recent requests list implementation + RR recent_requests; + // functions + void move_test_offset_ptr(); + void reset_offsets(); + uint64_t find_max_score(); + bool ok_to_prefetch(); + void print_scores(); + // private vars + bool monitor_GETS_; + bool monitor_GETX_; + uint32_t degree_; + std::vector> offset_scores; + int rounds_to_wait; + uint64_t current_round; + uint64_t test_offset_index; + uint64_t current_offset; +#ifndef TEST + Counter prof_emitted_prefetches_; +#endif +}; + +#endif diff --git a/src/init.cpp b/src/init.cpp index 2a6431a43..223867f6c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -53,6 +53,7 @@ #include "mem_ctrls.h" #include "network.h" #include "next_line_prefetcher.h" +#include "best_offset_prefetcher.h" #include "null_core.h" #include "ooo_core.h" #include "part_repl_policies.h" @@ -489,6 +490,27 @@ CacheGroup* BuildCacheGroup(Config& config, const string& name, bool isTerminal) degree)); } return cgp; + } else if (prefetch_type.compare("BO") == 0) { + bool monitor_loads = config.get(prefix + "monitorLoads", true); //really 'GETS' + bool monitor_stores = config.get(prefix + "monitorStores", true); //really 'GETX' + uint32_t degree = config.get(prefix + "degree", 0); + g_string target_cache = config.get(prefix + "target", ""); + uint32_t prefetchers = config.get(prefix + "prefetchers", 1); + if (target_cache.empty()) { + panic("Unspecified target cache for prefetcher '%s'", name.c_str()); + } + cg.resize(prefetchers); + for (uint32_t i = 0; i < prefetchers; i++) { + stringstream ss; + ss << name << '-' << i; + g_string full_name(ss.str().c_str()); + cg[i].emplace_back(new BestOffsetPrefetcher(full_name, + target_cache, + monitor_loads, + monitor_stores, + degree)); + } + return cgp; } else { panic("Unknown prefetcher type '%s'", prefetch_type.c_str()); } diff --git a/src/trace_init.cpp b/src/trace_init.cpp index 4cd59a28f..ecabfaeb2 100644 --- a/src/trace_init.cpp +++ b/src/trace_init.cpp @@ -53,6 +53,7 @@ #include "mem_ctrls.h" #include "network.h" #include "next_line_prefetcher.h" +#include "best_offset_prefetcher.h" #include "null_core.h" #include "ooo_core.h" #include "part_repl_policies.h" @@ -564,6 +565,27 @@ CacheGroup* BuildCacheGroup(Config& config, const string& name, bool isTerminal) degree)); } return cgp; + } else if (prefetch_type == "BO"){ + bool monitor_loads = config.get(prefix + "monitorLoads", true); //really 'GETS' + bool monitor_stores = config.get(prefix + "monitorStores", true); //really 'GETX' + uint32_t degree = config.get(prefix + "degree", 0); + g_string target_cache = config.get(prefix + "target", ""); + uint32_t prefetchers = config.get(prefix + "prefetchers", 1); + if (target_cache.empty()) { + panic("Unspecified target cache for prefetcher '%s'", name.c_str()); + } + cg.resize(prefetchers); + for (uint32_t i = 0; i < prefetchers; i++) { + stringstream ss; + ss << name << '-' << i; + g_string full_name(ss.str().c_str()); + cg[i].emplace_back(new BestOffsetPrefetcher(full_name, + target_cache, + monitor_loads, + monitor_stores, + degree)); + } + return cgp; } else { panic("Unknown prefetcher type '%s'", prefetch_type.c_str()); } diff --git a/src/trace_reader_memtrace.cpp b/src/trace_reader_memtrace.cpp index 22fb09d48..0d30fbaaf 100644 --- a/src/trace_reader_memtrace.cpp +++ b/src/trace_reader_memtrace.cpp @@ -76,7 +76,7 @@ void TraceReaderMemtrace::binaryGroupPathIs(const std::string &_path) { error.c_str()); return; } - module_mapper_ = module_mapper_t::create(directory_.modfile_bytes, + module_mapper_ = module_mapper_t::create(directory_.modfile_bytes_, #ifdef ZSIM_USE_YT parse_buildid_string, #else From 11bbd99bafaf050152ec4a914cd756811683748b Mon Sep 17 00:00:00 2001 From: Maxwell Bradley Date: Wed, 29 Jul 2020 17:36:20 +0000 Subject: [PATCH 2/5] Cleaned up code a bit. --- src/best_offset_prefetcher.cpp | 9 --------- src/best_offset_prefetcher.h | 6 +----- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/best_offset_prefetcher.cpp b/src/best_offset_prefetcher.cpp index 8565960f8..02ff17dd4 100644 --- a/src/best_offset_prefetcher.cpp +++ b/src/best_offset_prefetcher.cpp @@ -21,9 +21,6 @@ */ #include "best_offset_prefetcher.h" -#define L3_ACCESS_TIME 49 -//#define L3_ACCESS_TIME 27 -//#define L3_ACCESS_TIME 30 // helper to reset offset scores to 0 void BestOffsetPrefetcher::reset_offsets(){ @@ -175,9 +172,6 @@ uint64_t BestOffsetPrefetcher::access(MemReq& _req) { return resp_cycle; //Ignore other requests } -#ifdef SIMPLE_PC - if (_req.pc == 140577589630834){ -#endif // if not an instruction fetch, monitored, or a speculative request // execute a prefetch // need to figure out if the response cycle is going to DRAM or not @@ -192,9 +186,6 @@ uint64_t BestOffsetPrefetcher::access(MemReq& _req) { // run the learning algo with the current cycle in mind learn(req.lineAddr, req.cycle); } -#ifdef SIMPLE_PC - } -#endif return resp_cycle; } diff --git a/src/best_offset_prefetcher.h b/src/best_offset_prefetcher.h index 74e434cd9..8b9370feb 100644 --- a/src/best_offset_prefetcher.h +++ b/src/best_offset_prefetcher.h @@ -20,8 +20,6 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -//#define TEST -//#define SIMPLE_PC #ifndef BEST_OFFSET_PREFETCHER_H_ #define BEST_OFFSET_PREFETCHER_H_ @@ -42,14 +40,12 @@ #include #endif +const uint64_t L3_ACCESS_TIME = 49; // num run throughs to make before round over const uint64_t RND_MAX = 100; -//const uint64_t RND_MAX = 50; // max score before round ended ///const uint64_t MAX_SCORE = 10; const uint64_t MAX_SCORE = 31; -// score to signal to turn off prefetcher -//const uint64_t BAD_SCORE = 5; const uint64_t BAD_SCORE = 1; // offset to start prefetching with const uint64_t INIT_OFFSET = 1; From 8877aef3de8c0cd2f144b559f82e066cb2b6d357 Mon Sep 17 00:00:00 2001 From: Maxwell Bradley Date: Sat, 1 Aug 2020 17:53:03 +0000 Subject: [PATCH 3/5] Cleand up syntax of source files. Next step before pull request approved is to add configurability to the prefetcher from the configuration files. --- src/best_offset_prefetcher.cpp | 40 ++++++++++++++++++++++++---------- src/best_offset_prefetcher.h | 11 ---------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/best_offset_prefetcher.cpp b/src/best_offset_prefetcher.cpp index 02ff17dd4..c67d65ca5 100644 --- a/src/best_offset_prefetcher.cpp +++ b/src/best_offset_prefetcher.cpp @@ -17,11 +17,32 @@ * details. * * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * this program. If not, see . + */ #include "best_offset_prefetcher.h" +/* +The Best Offset Prefetcher is a prefetching algorithm proposed in the paper +Best Offset Hardware Prefetching written by Pierre Michaud. It is a non-PC +based prefetching algorithm that propes offsets from an access address +and selects an offset that generates the most timely prefetches. The prefetcher +monitors L2 misses and prefetches into the L3 cache. + +A brief API level description: an access comes in (access function), and the amount +time of until that prefetch is ready is calculated based on the expected time until the access will +make it to the cache. If the time for the prefetch is greater than the L3 access time +(signalling that the access is going to DRAM rather than the L3 cache), then one of the +offsets within the page (0-63) is tested to see if a prefetch to a previous access + that +test offset would have generated a timely prefetch. If so, then the "score" associated with +that offset is increased. When an offset's "score" approaches the maximum value or +all of the offsets have been gone through, the offset with the highest score is used to +prefetch from all the accesses that come in. All future accesses are prefetched with that +offset from that point onwards, hopefully generating timely prefetches, and the next round of +learning begins. +*/ + // helper to reset offset scores to 0 void BestOffsetPrefetcher::reset_offsets(){ for (auto& i : offset_scores){ @@ -29,7 +50,7 @@ void BestOffsetPrefetcher::reset_offsets(){ } } void BestOffsetPrefetcher::print_scores(){ - std::cout << std::endl; + // line break to distinguish between the last print std::cout << std::endl; for (auto& i : offset_scores){ if (i.second != 0) @@ -65,8 +86,7 @@ void BestOffsetPrefetcher::move_test_offset_ptr(){ // reset the pointer and set the round up this->test_offset_index = 0; this->current_round++; - // if max out the nuber of rounds, - // reset the prefetcher + // if max out the nuber of rounds, reset the prefetcher if (this->current_round == ( RND_MAX + 1 ) ){ this->current_offset = find_max_score(); reset_prefetcher(); @@ -85,12 +105,7 @@ void BestOffsetPrefetcher::learn(uint64_t addr, uint64_t cycle){ // if we're going off the page, dont' get it uint64_t base_addr = addr - test_offset; // if we're going off the page, dont' get it - if ( ((addr - test_offset) < 0 ) - or - ( - // if trying to check on another page, don't do it - (addr & PAGE_MASK) != ((base_addr) & PAGE_MASK) - )){ + if ( ((addr - test_offset) < 0 ) || ( (addr & PAGE_MASK) != ((base_addr) & PAGE_MASK))){ // move this offset along move_test_offset_ptr(); return; @@ -121,7 +136,10 @@ BestOffsetPrefetcher::BestOffsetPrefetcher(const g_string& _name, bool _monitor_GETS, bool _monitor_GETX, uint32_t _degree): CachePrefetcher(_name, _target), monitor_GETS_(_monitor_GETS), - monitor_GETX_(_monitor_GETX), degree_(_degree) { + monitor_GETX_(_monitor_GETX), degree_(1) { + if (_degree != 1) { + std::cout << "Ignoring degree parameter, using default degree of 1" <recent_requests = RR(); this->offset_scores = std::vector>(); diff --git a/src/best_offset_prefetcher.h b/src/best_offset_prefetcher.h index 8b9370feb..09745ad60 100644 --- a/src/best_offset_prefetcher.h +++ b/src/best_offset_prefetcher.h @@ -24,7 +24,6 @@ #ifndef BEST_OFFSET_PREFETCHER_H_ #define BEST_OFFSET_PREFETCHER_H_ -#ifndef TEST #include "cache_prefetcher.h" #include "filter_cache.h" #include "g_std/g_string.h" @@ -32,19 +31,11 @@ #include "stats.h" #include "hash.h" // hopefully don't need to include this for tests in the .cpp -#else -#include -#include -#include -#include -#include -#endif const uint64_t L3_ACCESS_TIME = 49; // num run throughs to make before round over const uint64_t RND_MAX = 100; // max score before round ended -///const uint64_t MAX_SCORE = 10; const uint64_t MAX_SCORE = 31; const uint64_t BAD_SCORE = 1; // offset to start prefetching with @@ -109,9 +100,7 @@ class BestOffsetPrefetcher : public CachePrefetcher { uint64_t current_round; uint64_t test_offset_index; uint64_t current_offset; -#ifndef TEST Counter prof_emitted_prefetches_; -#endif }; #endif From 2ee0d8d0c76b56eee7a1b1642c20b0a88d2f7e8d Mon Sep 17 00:00:00 2001 From: Maxwell Bradley Date: Wed, 29 Jul 2020 17:30:02 +0000 Subject: [PATCH 4/5] Added best offset prefetcher source files to forked branch. Cleaned up code a bit. Cleand up syntax of source files. Next step before pull request approved is to add configurability to the prefetcher from the configuration files. Fixed up code to standards. Added best offset prefetcher source files to forked branch. --- src/SConscript | 1 + src/best_offset_prefetcher.cpp | 315 +++++++++++++++++++++++++++++++++ src/best_offset_prefetcher.h | 102 +++++++++++ src/init.cpp | 32 ++++ src/trace_init.cpp | 32 ++++ src/trace_reader_memtrace.cpp | 2 +- 6 files changed, 483 insertions(+), 1 deletion(-) create mode 100644 src/best_offset_prefetcher.cpp create mode 100644 src/best_offset_prefetcher.h diff --git a/src/SConscript b/src/SConscript index 28593a31c..2bf2e4bba 100644 --- a/src/SConscript +++ b/src/SConscript @@ -28,6 +28,7 @@ traceSrcs = [ "monitor.cpp", "network.cpp", "next_line_prefetcher.cpp", + "best_offset_prefetcher.cpp", "null_core.cpp", "ooo_core.cpp", "ooo_core_recorder.cpp", diff --git a/src/best_offset_prefetcher.cpp b/src/best_offset_prefetcher.cpp new file mode 100644 index 000000000..e0e7fd9b3 --- /dev/null +++ b/src/best_offset_prefetcher.cpp @@ -0,0 +1,315 @@ +/** $lic$ + * This file is part of zsim. + * + * zsim is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, version 2. + * + * If you use this software in your research, we request that you reference + * the zsim paper ("ZSim: Fast and Accurate Microarchitectural Simulation of + * Thousand-Core Systems", Sanchez and Kozyrakis, ISCA-40, June 2013) as the + * source of the simulator in any publications that use this software, and that + * you send us a citation of your work. + * + * zsim is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + + */ + +#include "best_offset_prefetcher.h" + +/* +* The Best Offset Prefetcher is a prefetching algorithm proposed in the paper +* Best Offset Hardware Prefetching written by Pierre Michaud. It is a non-PC +* based prefetching algorithm that propes offsets from an access address +* and selects an offset that generates the most timely prefetches. The prefetcher +* monitors L2 misses and prefetches into the L3 cache. +* +* A brief API level description: an access comes in (access function), and the amount +* time of until that prefetch is ready is calculated based on the expected time until the access will +* make it to the cache. If the time for the prefetch is greater than the L3 access time +* (signalling that the access is going to DRAM rather than the L3 cache), then one of the +* offsets within the page (0-63) is tested to see if a prefetch to a previous access + that +* test offset would have generated a timely prefetch. If so, then the "score" associated with +* that offset is increased. When an offset's "score" approaches the maximum value or +* all of the offsets have been gone through, the offset with the highest score is used to +* prefetch from all the accesses that come in. All future accesses are prefetched with that +* offset from that point onwards, hopefully generating timely prefetches, and the next round of +* learning begins. +* +* Note that the implemenation of the recent requests table is more complex than that of the +* algorithm proposed in the paper. The existence of addresses is checked against a hash map +* that tracks a queue of all the most recent requests. When the list gets to capacity, +* the oldest item at the front of the list is removed, and its associated entry in the hash +* map is deleted as well. This leads to a more consistent prefetching offset than the method that +* the paper proposed, which is to delete all history of the last round by completely clearing +* the recent request table. + +*/ + +// helper function to reset offset scores to 0 +void BestOffsetPrefetcher::resetOffsets(){ + for (auto& i : offset_scores_){ + i.second = 0; + } +} + +// function used to print all scores in the ofset list +void BestOffsetPrefetcher::printScores(){ + // line break to distinguish between the last print + std::cout << std::endl; + for (auto& i : offset_scores_){ + if (i.second != 0) + std::cout << "Offset: " << i.first << " Score: " << i.second << std::endl; + } +} + +// function to reset the prefetcher, updating stats, resetting rounds and offsets +void BestOffsetPrefetcher::resetPrefetcher(){ + // compute a moving average of the amount of rounds per phase + average_rounds__ += current_round_ / (++total_phases_); + // set the counter value + average_rounds_.set(average_rounds__); + // return all offsets to 0 + resetOffsets(); + // set current round back to 0 + this->current_round_ = 0; + // set the test offset back to the end index (for timely prefetches) + this->test_offset_index_ = 0; +} + +// function to fin the most successful offset of the last learning round +uint64_t BestOffsetPrefetcher::findMaxScore(){ + // set the max score as 0 to start + std::pair max_score_ = std::pair(1, 0); + // run through all the offsets + for (auto s : offset_scores_){ + if (s.second >= max_score_.second){ + // set the offset to the offset value + max_score_.first = s.first; + // set the second to the score + max_score_.second = s.second; + } + } + // return the highest scoring offset in the list + return max_score_.first; +} + +// function to change the current offset that we are testing +void BestOffsetPrefetcher::moveTestOffsetPtr(){ + // if the current offset is the total minus 1 + if (this->test_offset_index_ == (num_offsets - 1) ){ + // reset the pointer and set the round up + this->test_offset_index_ = 0; + this->current_round_++; + // if max out the nuber of rounds, reset the prefetcher + if (this->current_round_ == ( this->round_max_ + uint64_t(1) ) ){ + this->current_offset_ = findMaxScore(); + resetPrefetcher(); + } + } + else{ + // move address in test_offset_point along + this->test_offset_index_++; + } +} + + +// main learning function for the best offset prefetching algorithm +void BestOffsetPrefetcher::learn(uint64_t _addr, uint64_t _cycle){ + // offset to currently test for a base address existing in the recent requests table + uint64_t test_offset = (this->offset_scores_[this->test_offset_index_]).first; + // if going off the page by subtracing the offset, don't consider the offset + uint64_t base_addr = _addr - test_offset; + if ( ((_addr - test_offset) < 0 ) || ( (_addr & page_mask) != ((base_addr) & page_mask))){ + moveTestOffsetPtr(); + return; + } + + // check if address in the recent requests table if on current page + if (this->recent_requests_.exists(base_addr, _cycle) == true){ + // increase the statistic for the number of recent request hits + recent_requests_hits_.inc(); + // if increasing that index by 1 would put us over the max score reset the prefetcher + if (this->offset_scores_[this->test_offset_index_].second == (this->max_score_ - uint64_t(1))){ + // function to print scores at the end of the the round. useful for debugging + //printScores(); + // increase the score for that offset + this->current_offset_ = this->offset_scores_[this->test_offset_index_].first; + // start a new learning phase with that new offset used for prefetching + resetPrefetcher(); + return; + } + // otherwise, if found the address but not maxed out, increment its score + else{ + this->offset_scores_[this->test_offset_index_].second++; + } + } + moveTestOffsetPtr(); +} + +BestOffsetPrefetcher::BestOffsetPrefetcher(const g_string& _name, + const g_string& _target, + bool _monitor_GETS, + bool _monitor_GETX, + uint32_t _degree, + uint64_t _round_max, + uint64_t _max_score, + uint64_t _init_offset, + uint64_t _target_latency + ): + CachePrefetcher(_name, _target), monitor_GETS_(_monitor_GETS), + monitor_GETX_(_monitor_GETX), degree_(1), round_max_(_round_max), + max_score_(_max_score), init_offset_(_init_offset), target_latency_(_target_latency) + { + + // BO only works with a degree of 1. Ignore any other value + if (_degree != 1) { + std::cout << "Ignoring degree parameter, using default degree of 1" <recent_requests_ = RR(); + this->recent_requests_.max_size_ = computed_max_size; + this->offset_scores_ = std::vector>(); + // load up the offset scores table with the offset and score + for (uint64_t i = 1; i <= num_offsets; ++i){ + offset_scores_.push_back(std::pair(i, 0)); + } + // set the current round to 0 of the learning phase + this->current_round_ = 0; + // set the first test offset to the first index in the offset list + this->test_offset_index_ = 0; + // set the prefetch offset to its init value (defined in config file) + this->current_offset_ = this->init_offset_; +} + +// create the stats for the prefetcher with clean data +void BestOffsetPrefetcher::initStats(AggregateStat* _parentStat) { + AggregateStat* s = new AggregateStat(); + s->init(name_.c_str(), "Best Offset prefetcher stats"); + prof_emitted_prefetches_.init("pf", "Emitted prefetches"); + recent_requests_hits_.init("bh", "Base addresses found in the recent requests table"); + average_rounds_.init("ar", "Average rounds per learning phase"); + s->append(&prof_emitted_prefetches_); + s->append(&recent_requests_hits_); + s->append(&average_rounds_); + _parentStat->append(s); +} + +// main entry point of best offset +uint64_t BestOffsetPrefetcher::access(MemReq& _req) { + MemReq req = _req; + req.childId = childId_; + // create a hash for which cache bank to access + uint32_t bank = CacheBankHash::hash(req.lineAddr, parents_.size()); + // if a speculative request, return and don't prefetch + uint64_t resp_cycle = parents_[bank]->access(req); + + if (_req.flags & MemReq::SPECULATIVE) { + return resp_cycle; + } + //Only do data prefetches for now + //if an instruction fetch, don't prefetch + if (req.flags & MemReq::IFETCH) { + return resp_cycle; + } + // if not monitored, don't prefetch either + bool monitored = (monitor_GETS_ && _req.type == GETS) || + (monitor_GETX_ && _req.type == GETX); + if (!monitored) { + return resp_cycle; //Ignore other requests + } + + // if not an instruction fetch, monitored, or a speculative request, execute a prefetch + prefetch(_req); + // run the prefetch on the address with the current offset + uint64_t time_to_fetch = resp_cycle - req.cycle; + // put the line address and the completion time in the RR + if (time_to_fetch >= (this->target_latency_ + coherency_time)){ + this->recent_requests_.insert(req.lineAddr, resp_cycle); + // if it's going to DRAM, put it in the RR + // otherwise, put it in the L3 + // run the learning algo with the current cycle in mind + learn(req.lineAddr, req.cycle); + } + return resp_cycle; +} + +// prefetch function to schedule prefetches +void BestOffsetPrefetcher::prefetch(MemReq& _req) { + auto &queue_info = d_caches_[_req.srcId]; //This is pair + uint64_t prefetch_location = _req.lineAddr + this->current_offset_; + if ((prefetch_location & page_mask) == (_req.lineAddr & page_mask)){ + queue_info.first->schedulePrefetch(prefetch_location, queue_info.second); + // increment the number of prefetches made + prof_emitted_prefetches_.inc(); + } +} +// function to insert an address into the recent requests table +void RR::insert(uint64_t _addr, uint64_t _cycle){ + // to handle deleting + if (this->list.size() == max_size_){ + this->deletion(); + } + // put the most recent address in the back of the list + this->list.push_back(std::pair(_addr, id)); + // see if this address already exists in the map + auto found = this->hash.find(_addr); + // if the thing exists in the map already + if (found != this->hash.end()){ + // set the hash id to the last element in the list's id + found->second.first = this->list.back().second; + }else { // if the map doesn't have this already + // put the address of the back of the list in the map + auto new_pair = std::make_pair(_addr, std::pair(id++, _cycle)); + this->hash.insert(new_pair); + } + // increase the size of the list + this->size_++; +} + +// check if an address exists in the recent requests table +bool RR::exists(uint64_t _addr, uint64_t _cycle){ + auto found = this->hash.find(_addr); + if (found != this->hash.end()){ + // make sure the completion cycle for the request is less than the current one + uint64_t completion = found->second.second; + if (completion <= _cycle){ + return true; + } + } + return false; +} + +// function to delete the front of the recent requests table +void RR::deletion(){ + // get the front of the list (thing to delete) + std::pair front = list.front(); + // find the corresponding address in the hash map + auto found = hash.find(front.first); + // should always be true + if (found != hash.end()){ + // check if the map entry is pointing to the front + if (found->second.first== list.front().second){ + // if it's pointing to the front, delete it + hash.erase(front.first); + } + // remove the first element from the list + list.pop_front(); + } + this->size_--; +} + +// init function for the recent requests table +RR::RR() + { + this->hash = std::map>(); + this->list = std::deque>(); +} + diff --git a/src/best_offset_prefetcher.h b/src/best_offset_prefetcher.h new file mode 100644 index 000000000..e3f9376c3 --- /dev/null +++ b/src/best_offset_prefetcher.h @@ -0,0 +1,102 @@ +/** $lic$ + * +* This file is part of zsim. + * + * zsim is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, version 2. + * + * If you use this software in your research, we request that you reference + * the zsim paper ("ZSim: Fast and Accurate Microarchitectural Simulation of + * Thousand-Core Systems", Sanchez and Kozyrakis, ISCA-40, June 2013) as the + * source of the simulator in any publications that use this software, and that + * you send us a citation of your work. + * + * zsim is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/* +This file contains the function declarations of both the best offset prefetcher +as well as the recent requests table implemenation. +*/ + + +#ifndef BEST_OFFSET_PREFETCHER_H_ +#define BEST_OFFSET_PREFETCHER_H_ + +#include "cache_prefetcher.h" +#include "filter_cache.h" +#include "g_std/g_string.h" +#include "memory_hierarchy.h" +#include "stats.h" +#include "hash.h" + +// global constants. Note that these are either not configurable or should not be changed. +const uint64_t coherency_time = 22; +const uint64_t num_offsets = 63; +const uint64_t page_size = pow(2, 12); +const uint64_t page_mask = 0xFFFFFFFFFFFFFFFF - page_size + 1; + +// declarations for the recent requests table implemenation +class RR { + public: + RR(); + void insert(uint64_t _addr, uint64_t _cycle); + bool exists(uint64_t _addr, uint64_t _cycle); + uint64_t size_ = 0; + uint64_t max_size_; + private: + std::map> hash; + std::deque> list; + void deletion(); + uint64_t id = 0; +}; + +class BestOffsetPrefetcher : public CachePrefetcher { +public: + explicit BestOffsetPrefetcher(const g_string& _name, const g_string& _target, + bool _monitor_GETS, bool _monitor_GETX, + uint32_t _degree, + uint64_t _round_max, + uint64_t _max_score, + uint64_t _init_offset, + uint64_t _target_latency_ + ); + void initStats(AggregateStat* _parentStat) override; + uint64_t access(MemReq& _req) override; + void prefetch(MemReq& _req) override; + void learn(uint64_t _addr, uint64_t _cycle); + + void resetPrefetcher(); +private: + // recent requests list object + RR recent_requests_; + // functions + void moveTestOffsetPtr(); + void resetOffsets(); + uint64_t findMaxScore(); + void printScores(); + // private vars + bool monitor_GETS_; + bool monitor_GETX_; + uint32_t degree_; + std::vector> offset_scores_; + uint64_t current_round_; + uint64_t test_offset_index_; + uint64_t current_offset_; + const int64_t round_max_; + const int64_t max_score_; + const int64_t init_offset_; + const uint64_t target_latency_; + uint64_t total_phases_ = 0; + float average_rounds__ = 0; + Counter prof_emitted_prefetches_, recent_requests_hits_, average_rounds_; +}; + +#endif diff --git a/src/init.cpp b/src/init.cpp index 2a6431a43..9d033a20f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -53,6 +53,7 @@ #include "mem_ctrls.h" #include "network.h" #include "next_line_prefetcher.h" +#include "best_offset_prefetcher.h" #include "null_core.h" #include "ooo_core.h" #include "part_repl_policies.h" @@ -489,6 +490,37 @@ CacheGroup* BuildCacheGroup(Config& config, const string& name, bool isTerminal) degree)); } return cgp; + } else if (prefetch_type == "BO"){ + //std::cout << "Prefix: " << prefix << std::endl; + bool monitor_loads = config.get(prefix + "monitorLoads", true); //really 'GETS' + bool monitor_stores = config.get(prefix + "monitorStores", true); //really 'GETX' + uint32_t degree = config.get(prefix + "degree", 0); + g_string target_cache = config.get(prefix + "target", ""); + uint32_t prefetchers = config.get(prefix + "prefetchers", 1); + uint64_t round_max = config.get(prefix + "round_max", 100); + uint64_t max_score = config.get(prefix + "max_score", 31); + uint64_t init_offset = config.get(prefix + "init_offset", 1); + g_string target_prefix = "sys.caches." + target_cache + ".latency"; + uint64_t target_latency = config.get(target_prefix.c_str()); + if (target_cache.empty()) { + panic("Unspecified target cache for prefetcher '%s'", name.c_str()); + } + cg.resize(prefetchers); + for (uint32_t i = 0; i < prefetchers; i++) { + stringstream ss; + ss << name << '-' << i; + g_string full_name(ss.str().c_str()); + cg[i].emplace_back(new BestOffsetPrefetcher(full_name, + target_cache, + monitor_loads, + monitor_stores, + degree, + round_max, + max_score, + init_offset, + target_latency + )); } + return cgp; } else { panic("Unknown prefetcher type '%s'", prefetch_type.c_str()); } diff --git a/src/trace_init.cpp b/src/trace_init.cpp index 4cd59a28f..489d0d495 100644 --- a/src/trace_init.cpp +++ b/src/trace_init.cpp @@ -53,6 +53,7 @@ #include "mem_ctrls.h" #include "network.h" #include "next_line_prefetcher.h" +#include "best_offset_prefetcher.h" #include "null_core.h" #include "ooo_core.h" #include "part_repl_policies.h" @@ -564,6 +565,37 @@ CacheGroup* BuildCacheGroup(Config& config, const string& name, bool isTerminal) degree)); } return cgp; + } else if (prefetch_type == "BO"){ + //std::cout << "Prefix: " << prefix << std::endl; + bool monitor_loads = config.get(prefix + "monitorLoads", true); //really 'GETS' + bool monitor_stores = config.get(prefix + "monitorStores", true); //really 'GETX' + uint32_t degree = config.get(prefix + "degree", 0); + g_string target_cache = config.get(prefix + "target", ""); + uint32_t prefetchers = config.get(prefix + "prefetchers", 1); + uint64_t round_max = config.get(prefix + "round_max", 100); + uint64_t max_score = config.get(prefix + "max_score", 31); + uint64_t init_offset = config.get(prefix + "init_offset", 1); + g_string target_prefix = "sys.caches." + target_cache + ".latency"; + uint64_t target_latency = config.get(target_prefix.c_str()); + if (target_cache.empty()) { + panic("Unspecified target cache for prefetcher '%s'", name.c_str()); + } + cg.resize(prefetchers); + for (uint32_t i = 0; i < prefetchers; i++) { + stringstream ss; + ss << name << '-' << i; + g_string full_name(ss.str().c_str()); + cg[i].emplace_back(new BestOffsetPrefetcher(full_name, + target_cache, + monitor_loads, + monitor_stores, + degree, + round_max, + max_score, + init_offset, + target_latency + )); } + return cgp; } else { panic("Unknown prefetcher type '%s'", prefetch_type.c_str()); } diff --git a/src/trace_reader_memtrace.cpp b/src/trace_reader_memtrace.cpp index 22fb09d48..0d30fbaaf 100644 --- a/src/trace_reader_memtrace.cpp +++ b/src/trace_reader_memtrace.cpp @@ -76,7 +76,7 @@ void TraceReaderMemtrace::binaryGroupPathIs(const std::string &_path) { error.c_str()); return; } - module_mapper_ = module_mapper_t::create(directory_.modfile_bytes, + module_mapper_ = module_mapper_t::create(directory_.modfile_bytes_, #ifdef ZSIM_USE_YT parse_buildid_string, #else From 1a1abdf089ef096cccd3c112bd378ac6bb1d114d Mon Sep 17 00:00:00 2001 From: Maxwell Bradley Date: Sun, 2 Aug 2020 22:17:28 +0000 Subject: [PATCH 5/5] Trying to get the pull request to go through --- src/best_offset_prefetcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/best_offset_prefetcher.cpp b/src/best_offset_prefetcher.cpp index e0e7fd9b3..b8dd7c12d 100644 --- a/src/best_offset_prefetcher.cpp +++ b/src/best_offset_prefetcher.cpp @@ -17,7 +17,7 @@ * details. * * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * this program. If not, see . */