blob: 83d68095ec20791eb27971c4574797acbc2a6c2c [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2015-2023 Google, LLC All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, LLC OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "cache_miss_analyzer.h"
#include <iostream>
#include <stdint.h>
namespace dynamorio {
namespace drmemtrace {
const char *cache_miss_stats_t::kNTA = "nta";
const char *cache_miss_stats_t::kT0 = "t0";
analysis_tool_t *
cache_miss_analyzer_create(const cache_simulator_knobs_t &knobs,
unsigned int miss_count_threshold, double miss_frac_threshold,
double confidence_threshold)
{
return new cache_miss_analyzer_t(knobs, miss_count_threshold, miss_frac_threshold,
confidence_threshold);
}
cache_miss_stats_t::cache_miss_stats_t(bool warmup_enabled, unsigned int line_size,
unsigned int miss_count_threshold,
double miss_frac_threshold,
double confidence_threshold)
: cache_stats_t(line_size, "", warmup_enabled, false)
, kLineSize(line_size)
, kMissCountThreshold(miss_count_threshold)
, kMissFracThreshold(miss_frac_threshold)
, kConfidenceThreshold(confidence_threshold)
{
// Setting this variable to true ensures that the dump_miss() function below
// gets called during cache simulation on a cache miss.
dump_misses_ = true;
}
void
cache_miss_stats_t::reset()
{
cache_stats_t::reset();
pc_cache_misses_.clear();
total_misses_ = 0;
}
void
cache_miss_stats_t::dump_miss(const memref_t &memref)
{
// If the operation causing the LLC miss is a memory read (load), insert
// the miss into the pc_cache_misses_ hash map and update
// the total_misses_ counter.
if (memref.data.type != TRACE_TYPE_READ) {
return;
}
// TODO i#6905: Consider incorporating PID information into the pc_cache_misses_ hash
// map and adjusting subsequent calculations that depend on this data.
const addr_t pc = memref.data.pc;
const addr_t addr = memref.data.addr / kLineSize;
pc_cache_misses_[pc].push_back(addr);
total_misses_++;
}
std::vector<prefetching_recommendation_t *>
cache_miss_stats_t::generate_recommendations()
{
unsigned int miss_count_threshold =
static_cast<unsigned int>(kMissFracThreshold * total_misses_);
if (miss_count_threshold > kMissCountThreshold) {
miss_count_threshold = kMissCountThreshold;
}
// Find loads that should be analyzed and analyze them.
std::vector<prefetching_recommendation_t *> recommendations;
for (auto &pc_cache_misses_it : pc_cache_misses_) {
std::vector<addr_t> &cache_misses = pc_cache_misses_it.second;
if (cache_misses.size() >= miss_count_threshold) {
const int stride = check_for_constant_stride(cache_misses);
if (stride != 0) {
prefetching_recommendation_t *recommendation =
new prefetching_recommendation_t;
recommendation->pc = pc_cache_misses_it.first;
recommendation->stride = stride;
recommendation->locality = kNTA;
recommendations.push_back(recommendation);
}
}
}
return recommendations;
}
int
cache_miss_stats_t::check_for_constant_stride(
const std::vector<addr_t> &cache_misses) const
{
std::unordered_map<int, int> stride_counts;
// Find and count all strides in the misses stream.
for (unsigned int i = 1; i < cache_misses.size(); ++i) {
int stride = static_cast<int>(cache_misses[i] - cache_misses[i - 1]);
if (stride != 0) {
stride_counts[stride]++;
}
}
// Find the most occurring stride.
int max_count = 0;
int max_count_stride = 0;
for (auto &stride_count : stride_counts) {
if (stride_count.second > max_count) {
max_count = stride_count.second;
max_count_stride = stride_count.first;
}
}
// Return the most occurring stride if it meets the confidence threshold.
stride_counts.clear();
if (max_count >= static_cast<int>(kConfidenceThreshold * cache_misses.size())) {
return max_count_stride * kLineSize;
} else {
return 0;
}
}
cache_miss_analyzer_t::cache_miss_analyzer_t(const cache_simulator_knobs_t &knobs,
unsigned int miss_count_threshold,
double miss_frac_threshold,
double confidence_threshold)
: cache_simulator_t(knobs)
{
if (!success_) {
return;
}
bool warmup_enabled_ = (knobs.warmup_refs > 0 || knobs.warmup_fraction > 0.0);
delete llcaches_["LL"]->get_stats();
ll_stats_ =
new cache_miss_stats_t(warmup_enabled_, knobs.line_size, miss_count_threshold,
miss_frac_threshold, confidence_threshold);
llcaches_["LL"]->set_stats(ll_stats_);
if (!knobs.LL_miss_file.empty()) {
recommendation_file_ = knobs.LL_miss_file;
}
}
std::vector<prefetching_recommendation_t *>
cache_miss_analyzer_t::generate_recommendations()
{
return ll_stats_->generate_recommendations();
}
bool
cache_miss_analyzer_t::print_results()
{
std::vector<prefetching_recommendation_t *> recommendations =
ll_stats_->generate_recommendations();
FILE *file = nullptr;
const bool write_to_file = !recommendation_file_.empty();
if (write_to_file) {
file = fopen(recommendation_file_.c_str(), "w");
}
std::cerr << "Cache miss analyzer results:\n";
for (auto &recommendation : recommendations) {
std::cerr << "pc=0x" << std::hex << recommendation->pc << std::dec
<< ", stride=" << recommendation->stride
<< ", locality=" << recommendation->locality << std::endl;
if (write_to_file) {
fprintf(file, "0x%lx,%d,%s\n", static_cast<unsigned long>(recommendation->pc),
recommendation->stride, recommendation->locality.c_str());
}
}
if (write_to_file) {
fclose(file);
}
// Reset the i/o format for subsequent tool invocations.
std::cerr << std::dec;
return true;
}
} // namespace drmemtrace
} // namespace dynamorio