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..b8dd7c12d --- /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