Skip to content

Commit

Permalink
i#6675: Collapse consecutive idle replay entries (#6673)
Browse files Browse the repository at this point in the history
Saves substantial file space in the drmemtrace scheduler's record-replay
file when cores are idling by combining consecutive idle entries.

Adds checks to all unit tests which create replay files; this requires
access to the non-public record format, done via a helper class.

Also tested on several real applications: for one the record file drops
from 34MB to 4K, matching the size of the as-traced schedule files. Even
tiny applications like threadsig show clear reductions. Consecutive idle
entries were ballooning these files. Large applications had multi-GB
files; with this fix they are orders of magnitude smaller.

Issue: #6471, #6675
Fixes: #6675
  • Loading branch information
derekbruening authored Feb 24, 2024
1 parent 41b55f2 commit 8022c8b
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 0 deletions.
23 changes: 23 additions & 0 deletions clients/drcachesim/scheduler/scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,23 @@ typedef dynamorio::drmemtrace::record_file_reader_t<std::ifstream>
default_record_file_reader_t;
#endif

std::string
replay_file_checker_t::check(archive_istream_t *infile)
{
// Ensure we don't have repeated idle records, which balloon the file size.
scheduler_t::schedule_record_t record;
bool prev_was_idle = false;
while (infile->read(reinterpret_cast<char *>(&record), sizeof(record))) {
if (record.type == scheduler_t::schedule_record_t::IDLE) {
if (prev_was_idle)
return "Error: consecutive idle records";
prev_was_idle = true;
} else
prev_was_idle = false;
}
return "";
}

/****************************************************************
* Specializations for scheduler_tmpl_t<reader_t>, aka scheduler_t.
*/
Expand Down Expand Up @@ -1628,6 +1645,12 @@ scheduler_tmpl_t<RecordType, ReaderType>::record_schedule_segment(
// We always use the current wall-clock time, as the time stored in the prior
// next_record() call can be out of order across outputs and lead to deadlocks.
uint64_t timestamp = get_time_micros();
if (type == schedule_record_t::IDLE &&
outputs_[output].record.back().type == schedule_record_t::IDLE) {
// Merge. We don't need intermediate timestamps when idle, and consecutive
// idle records quickly balloon the file.
return sched_type_t::STATUS_OK;
}
outputs_[output].record.emplace_back(type, input, start_instruction, stop_instruction,
timestamp);
// The stop is typically updated later in close_schedule_segment().
Expand Down
10 changes: 10 additions & 0 deletions clients/drcachesim/scheduler/scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@
namespace dynamorio { /**< General DynamoRIO namespace. */
namespace drmemtrace { /**< DrMemtrace tracing + simulation infrastructure namespace. */

/* For testing, where schedule_record_t is not accessible. */
class replay_file_checker_t {
public:
std::string
check(archive_istream_t *infile);
};

/**
* Schedules traced software threads onto simulated cpus.
* Takes in a set of recorded traces and maps them onto a new set of output
Expand Down Expand Up @@ -1611,6 +1618,9 @@ template <typename RecordType, typename ReaderType> class scheduler_tmpl_t {
// For online where we currently have to map dynamically observed thread ids
// to the 0-based shard index.
std::unordered_map<memref_tid_t, int> tid2shard_;

// Our testing class needs access to schedule_record_t.
friend class replay_file_checker_t;
};

/** See #dynamorio::drmemtrace::scheduler_tmpl_t. */
Expand Down
56 changes: 56 additions & 0 deletions clients/drcachesim/tests/scheduler_unit_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,14 @@ test_synthetic_time_quanta()
if (scheduler.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS)
assert(false);
}
{
replay_file_checker_t checker;
zipfile_istream_t infile(record_fname);
std::string res = checker.check(&infile);
if (!res.empty())
std::cerr << "replay file checker failed: " << res;
assert(res.empty());
}
{
// Replay.
std::vector<scheduler_t::input_reader_t> readers;
Expand Down Expand Up @@ -2285,6 +2293,14 @@ test_replay()
if (scheduler.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS)
assert(false);
}
{
replay_file_checker_t checker;
zipfile_istream_t infile(record_fname);
std::string res = checker.check(&infile);
if (!res.empty())
std::cerr << "replay file checker failed: " << res;
assert(res.empty());
}
// Now replay the schedule several times to ensure repeatability.
for (int outer = 0; outer < 5; ++outer) {
std::vector<scheduler_t::input_workload_t> sched_inputs;
Expand Down Expand Up @@ -2402,6 +2418,14 @@ test_replay_multi_threaded(const char *testdir)
if (scheduler.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS)
assert(false);
}
{
replay_file_checker_t checker;
zipfile_istream_t infile(record_fname);
std::string res = checker.check(&infile);
if (!res.empty())
std::cerr << "replay file checker failed: " << res;
assert(res.empty());
}
{
// Replay.
scheduler_t scheduler;
Expand Down Expand Up @@ -2740,6 +2764,14 @@ test_replay_skip()
if (scheduler.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS)
assert(false);
}
{
replay_file_checker_t checker;
zipfile_istream_t infile(record_fname);
std::string res = checker.check(&infile);
if (!res.empty())
std::cerr << "replay file checker failed: " << res;
assert(res.empty());
}
{
// Replay.
std::vector<scheduler_t::input_reader_t> readers;
Expand Down Expand Up @@ -2918,6 +2950,14 @@ test_replay_limit()
thread.join();
if (scheduler.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS)
assert(false);
}
{
replay_file_checker_t checker;
zipfile_istream_t infile(record_fname);
std::string res = checker.check(&infile);
if (!res.empty())
std::cerr << "replay file checker failed: " << res;
assert(res.empty());
for (int i = 0; i < NUM_OUTPUTS; ++i) {
std::cerr << "Output #" << i << " schedule: " << record_schedule[i] << "\n";
}
Expand Down Expand Up @@ -3005,6 +3045,14 @@ test_replay_limit()
thread.join();
if (scheduler.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS)
assert(false);
}
{
replay_file_checker_t checker;
zipfile_istream_t infile(record_fname);
std::string res = checker.check(&infile);
if (!res.empty())
std::cerr << "replay file checker failed: " << res;
assert(res.empty());
int switches = 0;
for (int i = 0; i < NUM_OUTPUTS; ++i) {
std::cerr << "Output #" << i << " schedule: " << record_schedule[i] << "\n";
Expand Down Expand Up @@ -3400,6 +3448,14 @@ test_inactive()
if (scheduler.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS)
assert(false);
}
{
replay_file_checker_t checker;
zipfile_istream_t infile(record_fname);
std::string res = checker.check(&infile);
if (!res.empty())
std::cerr << "replay file checker failed: " << res;
assert(res.empty());
}
{
// Replay.
std::vector<scheduler_t::input_reader_t> readers;
Expand Down

0 comments on commit 8022c8b

Please sign in to comment.