Skip to content

Commit

Permalink
Add support for custom hardware prefetchers in the cache simulator.
Browse files Browse the repository at this point in the history
  • Loading branch information
conNULL committed Sep 19, 2024
1 parent fd99b5e commit cebb4a0
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 20 deletions.
1 change: 1 addition & 0 deletions clients/drcachesim/common/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#define REPLACE_POLICY_FIFO "FIFO"
#define PREFETCH_POLICY_NEXTLINE "nextline"
#define PREFETCH_POLICY_NONE "none"
#define PREFETCH_POLICY_CUSTOM "custom"
#define CACHE_TYPE_INSTRUCTION "instruction"
#define CACHE_TYPE_DATA "data"
#define CACHE_TYPE_UNIFIED "unified"
Expand Down
57 changes: 42 additions & 15 deletions clients/drcachesim/simulator/cache_simulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,14 @@ cache_simulator_create(const std::string &config_file)
return sim;
}

cache_simulator_t::cache_simulator_t(const cache_simulator_knobs_t &knobs)
cache_simulator_t::cache_simulator_t(const cache_simulator_knobs_t &knobs, prefetcher_factory_t *custom_prefetcher_factory)
: simulator_t(knobs.num_cores, knobs.skip_refs, knobs.warmup_refs,
knobs.warmup_fraction, knobs.sim_refs, knobs.cpu_scheduling,
knobs.use_physical, knobs.verbose)
, knobs_(knobs)
, l1_icaches_(NULL)
, l1_dcaches_(NULL)
, custom_prefetcher_factory_(custom_prefetcher_factory)
, is_warmed_up_(false)
{
// XXX i#1703: get defaults from hardware being run on.
Expand All @@ -110,10 +111,18 @@ cache_simulator_t::cache_simulator_t(const cache_simulator_knobs_t &knobs)

if (knobs_.data_prefetcher != PREFETCH_POLICY_NEXTLINE &&
knobs_.data_prefetcher != PREFETCH_POLICY_NONE) {
// Unknown value.
error_string_ = " unknown data_prefetcher: '" + knobs_.data_prefetcher + "'";
success_ = false;
return;
if(knobs_.data_prefetcher == PREFETCH_POLICY_CUSTOM) {
if (custom_prefetcher_factory_ == nullptr) {
error_string_ = "custom prefetcher was requested but no factory was provided.";
success_ = false;
return;
}
} else {
// Unknown value.
error_string_ = " unknown data_prefetcher: '" + knobs_.data_prefetcher + "'";
success_ = false;
return;
}
}

bool warmup_enabled_ = ((knobs_.warmup_refs > 0) || (knobs_.warmup_fraction > 0.0));
Expand Down Expand Up @@ -165,9 +174,7 @@ cache_simulator_t::cache_simulator_t(const cache_simulator_knobs_t &knobs)
knobs_.L1D_assoc, (int)knobs_.line_size, (int)knobs_.L1D_size, llc,
new cache_stats_t((int)knobs_.line_size, "", warmup_enabled_,
knobs_.model_coherence),
knobs_.data_prefetcher == PREFETCH_POLICY_NEXTLINE
? new prefetcher_t((int)knobs_.line_size)
: nullptr,
get_prefetcher(knobs_.data_prefetcher),
cache_inclusion_policy_t::NON_INC_NON_EXC, knobs_.model_coherence,
(2 * i) + 1, snoop_filter_)) {
error_string_ = "Usage error: failed to initialize L1 caches. Ensure sizes "
Expand All @@ -189,12 +196,13 @@ cache_simulator_t::cache_simulator_t(const cache_simulator_knobs_t &knobs)
}
}

cache_simulator_t::cache_simulator_t(std::istream *config_file)
cache_simulator_t::cache_simulator_t(std::istream *config_file, prefetcher_factory_t* custom_prefetcher_factory)
: simulator_t()
, l1_icaches_(NULL)
, l1_dcaches_(NULL)
, snooped_caches_(NULL)
, snoop_filter_(NULL)
, custom_prefetcher_factory_(custom_prefetcher_factory)
, is_warmed_up_(false)
{
std::map<std::string, cache_params_t> cache_params;
Expand All @@ -211,9 +219,18 @@ cache_simulator_t::cache_simulator_t(std::istream *config_file)

if (knobs_.data_prefetcher != PREFETCH_POLICY_NEXTLINE &&
knobs_.data_prefetcher != PREFETCH_POLICY_NONE) {
// Unknown prefetcher type.
success_ = false;
return;
if(knobs_.data_prefetcher == PREFETCH_POLICY_CUSTOM) {
if (custom_prefetcher_factory_ == nullptr) {
error_string_ = "custom prefetcher was requested but no factory was provided.";
success_ = false;
return;
}
} else {
// Unknown value.
error_string_ = " unknown data_prefetcher: '" + knobs_.data_prefetcher + "'";
success_ = false;
return;
}
}

bool warmup_enabled_ = ((knobs_.warmup_refs > 0) || (knobs_.warmup_fraction > 0.0));
Expand Down Expand Up @@ -337,9 +354,7 @@ cache_simulator_t::cache_simulator_t(std::istream *config_file)
(int)cache_config.size, parent_,
new cache_stats_t((int)knobs_.line_size, cache_config.miss_file,
warmup_enabled_, is_coherent_),
cache_config.prefetcher == PREFETCH_POLICY_NEXTLINE
? new prefetcher_t((int)knobs_.line_size)
: nullptr,
get_prefetcher(cache_config.prefetcher),
inclusion_policy, is_coherent_, is_snooped ? snoop_id : -1,
is_snooped ? snoop_filter_ : nullptr, children)) {
error_string_ = "Usage error: failed to initialize the cache " + cache_name;
Expand Down Expand Up @@ -553,6 +568,18 @@ cache_simulator_t::process_memref(const memref_t &memref)
return true;
}

prefetcher_t *
cache_simulator_t::get_prefetcher(std::string prefetcher_name)
{
if (prefetcher_name == PREFETCH_POLICY_NEXTLINE) {
return new prefetcher_t((int)knobs_.line_size);
}
if (prefetcher_name == PREFETCH_POLICY_CUSTOM) {
return custom_prefetcher_factory_->create_prefetcher((int)knobs_.line_size);
}
return nullptr;
}

// Return true if the number of warmup references have been executed or if
// specified fraction of the llcaches_ has been loaded. Also return true if the
// cache has already been warmed up. When there are multiple last level caches
Expand Down
17 changes: 15 additions & 2 deletions clients/drcachesim/simulator/cache_simulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,20 @@ class cache_simulator_t : public simulator_t {
// This constructor is used when the cache hierarchy is configured
// using a set of knobs. It assumes a 2-level cache hierarchy with
// private L1 data and instruction caches and a shared LLC.
cache_simulator_t(const cache_simulator_knobs_t &knobs);
cache_simulator_t(const cache_simulator_knobs_t &knobs)
: cache_simulator_t(knobs, nullptr) {};

// This constructor is used when the arbitrary cache hierarchy is
// defined in a configuration file.
cache_simulator_t(std::istream *config_file);
cache_simulator_t(std::istream *config_file)
: cache_simulator_t(config_file, nullptr) {};

// Additional constructors that allow the user to specify a custom
// prefetcher.
cache_simulator_t(const cache_simulator_knobs_t &knobs,
prefetcher_factory_t *custom_prefetcher_factory);
cache_simulator_t(std::istream *config_file,
prefetcher_factory_t *custom_prefetcher_factory);

virtual ~cache_simulator_t();
bool
Expand Down Expand Up @@ -111,6 +120,7 @@ class cache_simulator_t : public simulator_t {
// Create a cache_t object with a specific replacement policy.
virtual cache_t *
create_cache(const std::string &name, const std::string &policy);
prefetcher_t * get_prefetcher(std::string prefetcher_name);

cache_simulator_knobs_t knobs_;

Expand All @@ -131,6 +141,9 @@ class cache_simulator_t : public simulator_t {

// Snoop filter tracks ownership of cache lines across private caches.
snoop_filter_t *snoop_filter_ = nullptr;

// Used to get prefetcher instances if the dataprefetcher knob is "custom".
prefetcher_factory_t *custom_prefetcher_factory_ = nullptr;

private:
bool is_warmed_up_;
Expand Down
2 changes: 0 additions & 2 deletions clients/drcachesim/simulator/prefetcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
#include "memref.h"
#include "caching_device.h"
#include "trace_entry.h"

namespace dynamorio {
namespace drmemtrace {

Expand All @@ -57,6 +56,5 @@ prefetcher_t::prefetch(caching_device_t *cache, const memref_t &memref_in)
memref.data.type = TRACE_TYPE_HARDWARE_PREFETCH;
cache->request(memref);
}

} // namespace drmemtrace
} // namespace dynamorio
8 changes: 7 additions & 1 deletion clients/drcachesim/simulator/prefetcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,16 @@ class prefetcher_t {
virtual void
prefetch(caching_device_t *cache, const memref_t &memref);

private:
protected:
int block_size_;
};

class prefetcher_factory_t {
public:
virtual prefetcher_t* create_prefetcher(int block_size){return nullptr;}
virtual ~prefetcher_factory_t(){}
};

} // namespace drmemtrace
} // namespace dynamorio

Expand Down
75 changes: 75 additions & 0 deletions clients/drcachesim/tests/drcachesim_unit_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "simulator/cache.h"
#include "simulator/cache_lru.h"
#include "simulator/cache_simulator.h"
#include "simulator/prefetcher.h"
#include "../common/memref.h"
#include "../common/utils.h"

Expand Down Expand Up @@ -272,7 +273,79 @@ unit_test_compulsory_misses()
assert(cache_sim.get_cache_metric(metric_name_t::MISSES, 1, 0,
cache_split_t::INSTRUCTION) == 6);
}
void
unit_test_nextline_prefetcher()
{
// Test all misses without a prefetcher.
cache_simulator_knobs_t knobs = make_test_knobs();
cache_simulator_t cache_sim(knobs);

for (int i = 0; i < 6; i++) {
if (!cache_sim.process_memref(make_memref(i * 64))) {
std::cerr << "drcachesim unit_test_nextline_prefetcher failed: "
<< cache_sim.get_error_string() << "\n";
exit(1);
}
}

assert(cache_sim.get_cache_metric(metric_name_t::MISSES, 1, 0) == 6);

// Test that every other miss is prevented with a nextline prefetcher.
knobs.data_prefetcher = "nextline";
cache_simulator_t nextline_cache_sim(knobs);
for (int i = 0; i < 6; i++) {
if (!nextline_cache_sim.process_memref(make_memref(i * 64))) {
std::cerr << "drcachesim unit_test_nextline_prefetcher failed: "
<< nextline_cache_sim.get_error_string() << "\n";
exit(1);
}
}

assert(nextline_cache_sim.get_cache_metric(metric_name_t::MISSES, 1, 0) == 3);
}


class next2line_prefetcher_factory_t : public prefetcher_factory_t {
public:
prefetcher_t *
create_prefetcher(int block_size) override
{
return new next2line_prefetcher_t(block_size);
}
private:
class next2line_prefetcher_t : public prefetcher_t {
public:
next2line_prefetcher_t(int block_size) : prefetcher_t(block_size) {}
void prefetch(caching_device_t *cache, const memref_t &memref_in) {
// We implement a simple 2 next-line prefetcher.
memref_t memref = memref_in;
memref.data.addr += block_size_;
memref.data.type = TRACE_TYPE_HARDWARE_PREFETCH;
cache->request(memref);
memref.data.addr += block_size_;
cache->request(memref);
}
};
};

void
unit_test_custom_prefetcher()
{
cache_simulator_knobs_t knobs = make_test_knobs();
knobs.data_prefetcher = "custom";
next2line_prefetcher_factory_t next2line_prefetcher_factory;
cache_simulator_t nextline_cache_sim(knobs, &next2line_prefetcher_factory);
// Test that every 2/3 misses are prevented with a next2line prefetcher.
for (int i = 0; i < 6; i++) {
if (!nextline_cache_sim.process_memref(make_memref(i * 64))) {
std::cerr << "drcachesim unit_test_custom_prefetcher failed: "
<< nextline_cache_sim.get_error_string() << "\n";
exit(1);
}
}

assert(nextline_cache_sim.get_cache_metric(metric_name_t::MISSES, 1, 0) == 2);
}
void
unit_test_child_hits()
{
Expand Down Expand Up @@ -858,6 +931,8 @@ test_main(int argc, const char *argv[])
unit_test_child_hits();
unit_test_cache_replacement_policy();
unit_test_core_sharded();
unit_test_nextline_prefetcher();
unit_test_custom_prefetcher();
return 0;
}

Expand Down

0 comments on commit cebb4a0

Please sign in to comment.