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