Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clients/drcachesim/analyzer_multi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ analyzer_multi_t::create_analysis_tool_from_options(const std::string &tool)
knobs.report_histogram = op_reuse_distance_histogram.get_value();
knobs.distance_threshold = op_reuse_distance_threshold.get_value();
knobs.report_top = op_report_top.get_value();
knobs.skip_list_distance = op_reuse_skip_dist.get_value();
knobs.skip_list_distance = op_reuse_skip_dist_deprecated.get_value();
knobs.distance_limit = op_reuse_distance_limit.get_value();
knobs.verify_skip = op_reuse_verify_skip.get_value();
knobs.histogram_bin_multiplier = op_reuse_histogram_bin_multiplier.get_value();
Expand Down
8 changes: 5 additions & 3 deletions clients/drcachesim/common/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -800,11 +800,12 @@ droption_t<bool> op_reuse_distance_histogram(
"Print the entire reuse distance histogram.",
"By default only the mean, median, and standard deviation of the reuse distances "
"are reported. This option prints out the full histogram of reuse distances.");
droption_t<unsigned int> op_reuse_skip_dist(
droption_t<unsigned int> op_reuse_skip_dist_deprecated(
DROPTION_SCOPE_FRONTEND, "reuse_skip_dist", 500,
"For performance tuning: distance between skip nodes.",
"Specifies the distance between nodes in the skip list. For optimal performance, "
"set this to a value close to the estimated average reuse distance of the dataset.");
"set this to a value close to the estimated average reuse distance of the dataset."
"DEPRECATED: skip lists are no longer used.");
droption_t<unsigned int> op_reuse_distance_limit(
DROPTION_SCOPE_FRONTEND, "reuse_distance_limit", 0,
"If nonzero, restricts distance tracking to the specified maximum distance.",
Expand All @@ -816,7 +817,8 @@ droption_t<bool> op_reuse_verify_skip(
"Use full list walks to verify the skip list results.",
"Verifies every skip list-calculated reuse distance with a full list walk. "
"This incurs significant additional overhead. This option is only available "
"in debug builds.");
"in debug builds."
"DEPRECATED: skip lists are no longer used.");
droption_t<double> op_reuse_histogram_bin_multiplier(
DROPTION_SCOPE_FRONTEND, "reuse_histogram_bin_multiplier", 1.00,
"When reporting histograms, grow bins geometrically by this multiplier.",
Expand Down
2 changes: 1 addition & 1 deletion clients/drcachesim/common/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ extern dynamorio::droption::droption_t<bool> op_add_noise_generator;
extern dynamorio::droption::droption_t<unsigned int> op_report_top;
extern dynamorio::droption::droption_t<unsigned int> op_reuse_distance_threshold;
extern dynamorio::droption::droption_t<bool> op_reuse_distance_histogram;
extern dynamorio::droption::droption_t<unsigned int> op_reuse_skip_dist;
extern dynamorio::droption::droption_t<unsigned int> op_reuse_skip_dist_deprecated;
extern dynamorio::droption::droption_t<unsigned int> op_reuse_distance_limit;
extern dynamorio::droption::droption_t<bool> op_reuse_verify_skip;
extern dynamorio::droption::droption_t<double> op_reuse_histogram_bin_multiplier;
Expand Down
109 changes: 109 additions & 0 deletions clients/drcachesim/tests/reuse_distance_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
*/

#include <iostream>
#include <functional>
#include <assert.h>

#include "../tools/reuse_distance.h"
Expand Down Expand Up @@ -507,6 +508,112 @@ data_histogram_test()
}
}

// Helper to verify all node sizes in the tree are consistent.
uint64_t
verify_sizes(line_ref_node_t *node)
{
if (node == nullptr)
return 0;
auto expected = verify_sizes(node->left) + verify_sizes(node->right) + 1;
assert(node->size == expected);
return expected;
}

// Test for splay tree.
void
splay_tree_test()
{
std::cerr << "splay_tree_test()\n";
constexpr uint64_t THRESHOLD = 4;
constexpr int NUM_NODES = 10;
constexpr uint64_t TEST_ADDRESS = 0x1000;
constexpr uint64_t TEST_DISTANCE_INCREMENT = 64;

line_ref_splay_t tree(THRESHOLD);
address_generator_t agen(TEST_ADDRESS, TEST_DISTANCE_INCREMENT);

// Test insertion and basic structure.
std::vector<line_ref_node_t *> nodes;
for (int i = 0; i < NUM_NODES; ++i) {
nodes.push_back(new line_ref_node_t(agen.next_address()));
tree.add_to_front(nodes.back());
assert(tree.root_ == nodes.back());
assert(tree.head_ == nodes.back());
assert(tree.tail_ == nodes[0]);
assert(tree.unique_lines_ == i + 1);
assert(tree.root_->size == i + 1);
}
verify_sizes(tree.root_);

// Test move_to_front and reuse distance calculation.
auto dist = tree.move_to_front(nodes[5]);
assert(dist == 4);
assert(tree.head_ == nodes[5]);
assert(nodes[5]->total_refs == 2);
verify_sizes(tree.root_);

dist = tree.move_to_front(nodes[0]);
assert(dist == NUM_NODES - 1);
assert(tree.head_ == nodes[0]);
verify_sizes(tree.root_);

dist = tree.move_to_front(nodes[0]);
assert(dist == 0);
assert(nodes[0]->total_refs == 3);

// Test gate mechanism and distant reference tracking.
assert(tree.gate_ != nullptr);
auto *distant_node = nodes[1]; // Should be beyond gate.
assert(tree.ref_is_distant(distant_node));
auto old_distant_refs = distant_node->distant_refs;
tree.move_to_front(distant_node);
assert(distant_node->distant_refs == old_distant_refs + 1);

auto *recent_node = tree.head_;
assert(!tree.ref_is_distant(recent_node));

// Test splay rotations maintain invariants.
tree.splay(nodes[3]);
assert(tree.root_ == nodes[3]);
verify_sizes(tree.root_);
tree.splay(nodes[7]);
assert(tree.root_ == nodes[7]);
verify_sizes(tree.root_);

// Test get_prev traversal.
auto *current = tree.tail_;
int count = 0;
while (current != nullptr) {
current = tree.get_prev(current);
++count;
assert(count <= NUM_NODES);
}
assert(count == NUM_NODES);
assert(tree.get_prev(nullptr) == nullptr);

// Test removal.
auto *to_remove = nodes[4];
tree.remove(to_remove);
assert(tree.root_->size == NUM_NODES - 1);
assert(to_remove->parent == nullptr && to_remove->left == nullptr);
delete to_remove;
nodes[4] = nullptr;
verify_sizes(tree.root_);

// Test prune_tail.
auto *old_tail = tree.tail_;
tree.prune_tail();
assert(tree.tail_ != old_tail);
assert(tree.root_->size == NUM_NODES - 2);
delete old_tail;
verify_sizes(tree.root_);

if (TEST_VERBOSE(1)) {
std::cerr << "Final tree size: " << tree.root_->size << "\n";
std::cerr << "Unique lines: " << tree.unique_lines_ << "\n";
}
}

int
test_main(int argc, const char *argv[])
{
Expand All @@ -516,6 +623,8 @@ test_main(int argc, const char *argv[])
simple_reuse_distance_test();
reuse_distance_limit_test();
data_histogram_test();
splay_tree_test();

return 0;
}

Expand Down
44 changes: 20 additions & 24 deletions clients/drcachesim/tools/reuse_distance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,11 @@ reuse_distance_t::initialize_shard_type(shard_type_t shard_type)
return "";
}

reuse_distance_t::shard_data_t::shard_data_t(uint64_t reuse_threshold, uint64_t skip_dist,
uint32_t distance_limit, bool verify)
reuse_distance_t::shard_data_t::shard_data_t(uint64_t reuse_threshold,
uint32_t distance_limit)
: distance_limit(distance_limit)
{
ref_list = std::unique_ptr<line_ref_list_t>(
new line_ref_list_t(reuse_threshold, skip_dist, verify));
ref_list = std::unique_ptr<line_ref_splay_t>(new line_ref_splay_t(reuse_threshold));
}

bool
Expand All @@ -117,8 +116,7 @@ void *
reuse_distance_t::parallel_shard_init_stream(int shard_index, void *worker_data,
memtrace_stream_t *stream)
{
auto shard = new shard_data_t(knobs_.distance_threshold, knobs_.skip_list_distance,
knobs_.distance_limit, knobs_.verify_skip);
auto shard = new shard_data_t(knobs_.distance_threshold, knobs_.distance_limit);
std::lock_guard<std::mutex> guard(shard_map_mutex_);
shard->core = stream->get_output_cpuid();
shard->tid = stream->get_tid();
Expand Down Expand Up @@ -166,12 +164,12 @@ reuse_distance_t::parallel_shard_memref(void *shard_data, const memref_t &memref
++shard->data_refs;
}
addr_t tag = memref.data.addr >> line_size_bits_;
std::unordered_map<addr_t, line_ref_t *>::iterator it =
std::unordered_map<addr_t, line_ref_node_t *>::iterator it =
shard->cache_map.find(tag);
if (it == shard->cache_map.end()) {
line_ref_t *ref = new line_ref_t(tag);
line_ref_node_t *ref = new line_ref_node_t(tag);
// insert into the map
shard->cache_map.insert(std::pair<addr_t, line_ref_t *>(tag, ref));
shard->cache_map.insert(std::pair<addr_t, line_ref_node_t *>(tag, ref));
// insert into the list
shard->ref_list->add_to_front(ref);
// See if the line we're adding was previously removed.
Expand Down Expand Up @@ -215,8 +213,7 @@ reuse_distance_t::process_memref(const memref_t &memref)
int shard_index = serial_stream_->get_shard_index();
const auto &lookup = shard_map_.find(shard_index);
if (lookup == shard_map_.end()) {
shard = new shard_data_t(knobs_.distance_threshold, knobs_.skip_list_distance,
knobs_.distance_limit, knobs_.verify_skip);
shard = new shard_data_t(knobs_.distance_threshold, knobs_.distance_limit);
shard->core = serial_stream_->get_output_cpuid();
shard->tid = serial_stream_->get_tid();
shard_map_[shard_index] = shard;
Expand All @@ -237,8 +234,8 @@ cmp_dist_key(const reuse_distance_t::distance_map_pair_t &l,
}

static bool
cmp_total_refs(const std::pair<addr_t, line_ref_t *> &l,
const std::pair<addr_t, line_ref_t *> &r)
cmp_total_refs(const std::pair<addr_t, line_ref_node_t *> &l,
const std::pair<addr_t, line_ref_node_t *> &r)
{
if (l.second->total_refs > r.second->total_refs)
return true;
Expand All @@ -252,8 +249,8 @@ cmp_total_refs(const std::pair<addr_t, line_ref_t *> &l,
}

static bool
cmp_distant_refs(const std::pair<addr_t, line_ref_t *> &l,
const std::pair<addr_t, line_ref_t *> &r)
cmp_distant_refs(const std::pair<addr_t, line_ref_node_t *> &l,
const std::pair<addr_t, line_ref_node_t *> &r)
{
if (l.second->distant_refs > r.second->distant_refs)
return true;
Expand Down Expand Up @@ -324,15 +321,15 @@ reuse_distance_t::print_shard_results(const shard_data_t *shard)
std::cerr << "\n";
std::cerr << "Reuse distance threshold = " << knobs_.distance_threshold
<< " cache lines\n";
std::vector<std::pair<addr_t, line_ref_t *>> top(knobs_.report_top);
std::vector<std::pair<addr_t, line_ref_node_t *>> top(knobs_.report_top);
std::partial_sort_copy(shard->cache_map.begin(), shard->cache_map.end(), top.begin(),
top.end(), cmp_total_refs);
std::cerr << "Top " << top.size() << " frequently referenced cache lines\n";
std::cerr << std::setw(18) << "cache line"
<< ": " << std::setw(17) << "#references " << std::setw(14)
<< "#distant refs"
<< "\n";
for (std::vector<std::pair<addr_t, line_ref_t *>>::iterator it = top.begin();
for (std::vector<std::pair<addr_t, line_ref_node_t *>>::iterator it = top.begin();
it != top.end(); ++it) {
if (it->second == NULL) // Very small app.
break;
Expand All @@ -350,7 +347,7 @@ reuse_distance_t::print_shard_results(const shard_data_t *shard)
<< ": " << std::setw(17) << "#references " << std::setw(14)
<< "#distant refs"
<< "\n";
for (std::vector<std::pair<addr_t, line_ref_t *>>::iterator it = top.begin();
for (std::vector<std::pair<addr_t, line_ref_node_t *>>::iterator it = top.begin();
it != top.end(); ++it) {
if (it->second == NULL) // Very small app.
break;
Expand Down Expand Up @@ -452,8 +449,7 @@ reuse_distance_t::get_aggregated_results()

// Otherwise, aggregate the per-shard data to get whole-trace data.
aggregated_results_ = std::unique_ptr<shard_data_t>(
new shard_data_t(knobs_.distance_threshold, knobs_.skip_list_distance,
knobs_.distance_limit, knobs_.verify_skip));
new shard_data_t(knobs_.distance_threshold, knobs_.distance_limit));
for (auto &shard : shard_map_) {
aggregated_results_->total_refs += shard.second->total_refs;
aggregated_results_->data_refs += shard.second->data_refs;
Expand Down Expand Up @@ -484,11 +480,11 @@ reuse_distance_t::get_aggregated_results()
}
for (const auto &entry : shard.second->cache_map) {
const auto &existing = aggregated_results_->cache_map.find(entry.first);
line_ref_t *ref;
line_ref_node_t *ref;
if (existing == aggregated_results_->cache_map.end()) {
ref = new line_ref_t(entry.first);
ref = new line_ref_node_t(entry.first);
aggregated_results_->cache_map.insert(
std::pair<addr_t, line_ref_t *>(entry.first, ref));
std::pair<addr_t, line_ref_node_t *>(entry.first, ref));
ref->total_refs = 0;
} else {
ref = existing->second;
Expand All @@ -507,7 +503,7 @@ reuse_distance_t::print_results()
std::cerr << TOOL_NAME << " aggregated results:\n";
print_shard_results(get_aggregated_results());

// For regular shards the line_ref_t's are deleted in ~line_ref_list_t.
// For regular shards the line_ref_node_t's are deleted in ~line_ref_splay_t.
for (auto &iter : get_aggregated_results()->cache_map) {
delete iter.second;
}
Expand Down
Loading
Loading