Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i#5843 scheduler: Add option to disable direct switches #6770

Merged
merged 3 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions clients/drcachesim/analyzer_multi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ analyzer_multi_tmpl_t<RecordType, ReaderType>::init_dynamic_schedule()
sched_ops.block_time_scale = op_sched_block_scale.get_value();
sched_ops.block_time_max = op_sched_block_max_us.get_value();
sched_ops.randomize_next_input = op_sched_randomize.get_value();
sched_ops.honor_direct_switches = !op_sched_disable_direct_switches.get_value();
#ifdef HAS_ZIP
if (!op_record_file.get_value().empty()) {
record_schedule_zip_.reset(new zipfile_ostream_t(op_record_file.get_value()));
Expand Down
8 changes: 8 additions & 0 deletions clients/drcachesim/common/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,14 @@ droption_t<bool> op_sched_randomize(
"set), and FIFO order and instead selects the next input randomly. "
"This is intended for experimental use in sensitivity studies.");

droption_t<bool> op_sched_disable_direct_switches(
abhinav92003 marked this conversation as resolved.
Show resolved Hide resolved
DROPTION_SCOPE_FRONTEND, "sched_disable_direct_switches", false,
"Ignore direct thread switch requests",
"Applies to -core_sharded and -core_serial. Disables switching to the recorded "
"targets of TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH system call metadata markers "
"and causes the associated system call to be treated like any other call with a "
"switch being determined by latency and the next input in the queue.");

// Schedule_stats options.
droption_t<uint64_t>
op_schedule_stats_print_every(DROPTION_SCOPE_ALL, "schedule_stats_print_every",
Expand Down
1 change: 1 addition & 0 deletions clients/drcachesim/common/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ extern dynamorio::droption::droption_t<std::string> op_cpu_schedule_file;
#endif
extern dynamorio::droption::droption_t<std::string> op_sched_switch_file;
extern dynamorio::droption::droption_t<bool> op_sched_randomize;
extern dynamorio::droption::droption_t<bool> op_sched_disable_direct_switches;
extern dynamorio::droption::droption_t<uint64_t> op_schedule_stats_print_every;
extern dynamorio::droption::droption_t<std::string> op_syscall_template_file;
extern dynamorio::droption::droption_t<uint64_t> op_filter_stop_timestamp;
Expand Down
3 changes: 2 additions & 1 deletion clients/drcachesim/scheduler/scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2781,7 +2781,8 @@ scheduler_tmpl_t<RecordType, ReaderType>::next_record(output_ordinal_t output,
// boundaries so we live with those being before the switch.
// XXX: Once we insert kernel traces, we may have to try harder
// to stop before the post-syscall records.
if (record_type_is_marker(record, marker_type, marker_value) &&
if (options_.honor_direct_switches &&
record_type_is_marker(record, marker_type, marker_value) &&
marker_type == TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH) {
memref_tid_t target_tid = marker_value;
auto it =
Expand Down
11 changes: 11 additions & 0 deletions clients/drcachesim/scheduler/scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,17 @@ template <typename RecordType, typename ReaderType> class scheduler_tmpl_t {
* ahead.
*/
bool read_inputs_in_init = true;
/**
* If true, the scheduler will attempt to switch to the recorded targets of
* #TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH system call metadata markers
* regardless of system call latency. If the target is not available, the
* current implementation will select the next available input in the regular
* scheduling queue, but in the future a forced migration may be applied for
* an input currently on another output. If false, the direct switch markers
* are ignored and only system call latency thresholds are used to determine
* switches.
*/
bool honor_direct_switches = true;
};

/**
Expand Down
109 changes: 75 additions & 34 deletions clients/drcachesim/tests/scheduler_unit_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3794,42 +3794,83 @@ test_direct_switch()
make_marker(TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH, TID_BASE + 3),
make_exit(TID_C),
};
std::vector<scheduler_t::input_reader_t> readers;
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_A)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_A);
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_B)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_B);
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_C)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_C);
// The string constructor writes "." for markers.
// We expect A's first switch to be to C even though B has an earlier timestamp.
// We expect C's direct switch to A to proceed immediately even though A still
// has significant blocked time left. But then after B is scheduled and finishes,
// we still have to wait for C's block time so we see idle underscores:
static const char *const CORE0_SCHED_STRING =
"...AA.........CC......A....BBBB.______________C...";
{
// Test the defaults with direct switches enabled.
std::vector<scheduler_t::input_reader_t> readers;
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_A)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_A);
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_B)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_B);
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_C)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_C);
// The string constructor writes "." for markers.
// We expect A's first switch to be to C even though B has an earlier timestamp.
// We expect C's direct switch to A to proceed immediately even though A still
// has significant blocked time left. But then after B is scheduled and finishes,
// we still have to wait for C's block time so we see idle underscores:
static const char *const CORE0_SCHED_STRING =
"...AA.........CC......A....BBBB.______________C...";
derekbruening marked this conversation as resolved.
Show resolved Hide resolved

std::vector<scheduler_t::input_workload_t> sched_inputs;
sched_inputs.emplace_back(std::move(readers));
scheduler_t::scheduler_options_t sched_ops(scheduler_t::MAP_TO_ANY_OUTPUT,
scheduler_t::DEPENDENCY_TIMESTAMPS,
scheduler_t::SCHEDULER_DEFAULTS,
/*verbosity=*/3);
sched_ops.quantum_duration = QUANTUM_DURATION;
// We use our mock's time==instruction count for a deterministic result.
sched_ops.quantum_unit = scheduler_t::QUANTUM_TIME;
sched_ops.blocking_switch_threshold = BLOCK_LATENCY;
sched_ops.block_time_scale = BLOCK_SCALE;
scheduler_t scheduler;
if (scheduler.init(sched_inputs, NUM_OUTPUTS, std::move(sched_ops)) !=
scheduler_t::STATUS_SUCCESS)
assert(false);
std::vector<std::string> sched_as_string =
run_lockstep_simulation(scheduler, NUM_OUTPUTS, TID_BASE, /*send_time=*/true);
for (int i = 0; i < NUM_OUTPUTS; i++) {
std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n";
std::vector<scheduler_t::input_workload_t> sched_inputs;
sched_inputs.emplace_back(std::move(readers));
scheduler_t::scheduler_options_t sched_ops(scheduler_t::MAP_TO_ANY_OUTPUT,
scheduler_t::DEPENDENCY_TIMESTAMPS,
scheduler_t::SCHEDULER_DEFAULTS,
/*verbosity=*/3);
sched_ops.quantum_duration = QUANTUM_DURATION;
// We use our mock's time==instruction count for a deterministic result.
sched_ops.quantum_unit = scheduler_t::QUANTUM_TIME;
sched_ops.blocking_switch_threshold = BLOCK_LATENCY;
sched_ops.block_time_scale = BLOCK_SCALE;
scheduler_t scheduler;
if (scheduler.init(sched_inputs, NUM_OUTPUTS, std::move(sched_ops)) !=
scheduler_t::STATUS_SUCCESS)
assert(false);
std::vector<std::string> sched_as_string =
run_lockstep_simulation(scheduler, NUM_OUTPUTS, TID_BASE, /*send_time=*/true);
for (int i = 0; i < NUM_OUTPUTS; i++) {
std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n";
}
assert(sched_as_string[0] == CORE0_SCHED_STRING);
}
{
// Test disabling direct switches.
std::vector<scheduler_t::input_reader_t> readers;
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_A)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_A);
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_B)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_B);
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_C)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_C);
// The string constructor writes "." for markers.
// We expect A's first switch to be to B with an earlier timestamp.
// We expect C's direct switch to A to not happen until A's blocked time ends.
static const char *const CORE0_SCHED_STRING =
"...AA.........BBBB....CC......___________________C...___A.";
derekbruening marked this conversation as resolved.
Show resolved Hide resolved

std::vector<scheduler_t::input_workload_t> sched_inputs;
sched_inputs.emplace_back(std::move(readers));
scheduler_t::scheduler_options_t sched_ops(scheduler_t::MAP_TO_ANY_OUTPUT,
scheduler_t::DEPENDENCY_TIMESTAMPS,
scheduler_t::SCHEDULER_DEFAULTS,
/*verbosity=*/3);
sched_ops.quantum_duration = QUANTUM_DURATION;
// We use our mock's time==instruction count for a deterministic result.
sched_ops.quantum_unit = scheduler_t::QUANTUM_TIME;
sched_ops.blocking_switch_threshold = BLOCK_LATENCY;
sched_ops.block_time_scale = BLOCK_SCALE;
sched_ops.honor_direct_switches = false;
scheduler_t scheduler;
if (scheduler.init(sched_inputs, NUM_OUTPUTS, std::move(sched_ops)) !=
scheduler_t::STATUS_SUCCESS)
assert(false);
std::vector<std::string> sched_as_string =
run_lockstep_simulation(scheduler, NUM_OUTPUTS, TID_BASE, /*send_time=*/true);
for (int i = 0; i < NUM_OUTPUTS; i++) {
std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n";
}
assert(sched_as_string[0] == CORE0_SCHED_STRING);
}
assert(sched_as_string[0] == CORE0_SCHED_STRING);
}

static void
Expand Down
Loading