Skip to content

Commit 7398552

Browse files
committed
i#7318 rebalance: Ignore bind-to-all
If an input is bound to every output, we now ignore those bindings, which eliminates complexities in initial output allocation where rebalancing is required to even things out (and that gets complicated by initially-blocked inputs) as well as removes overhead in the runqueue code during dynamic scheduling. Binding to every output is actually not uncommon as users have such bindings as a default value. As part of detecting bind-to-all, invalid bindings are now detected. Adds unit tests for the invalid bindings. Updates the unbalanced rebalance test to bind to all but one as binding to all is no longer unbalanced. Issue: #7318
1 parent 0bdf676 commit 7398552

File tree

2 files changed

+79
-3
lines changed

2 files changed

+79
-3
lines changed

clients/drcachesim/scheduler/scheduler_impl.cpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,21 @@ scheduler_impl_tmpl_t<RecordType, ReaderType>::init(
837837
int index = reader_info.tid2input[tid];
838838
input_info_t &input = inputs_[index];
839839
input.has_modifier = true;
840-
input.binding = modifiers.output_binding;
840+
// Check for valid bindings.
841+
for (output_ordinal_t bind : modifiers.output_binding) {
842+
if (bind < 0 || bind > output_count) {
843+
error_string_ =
844+
"output_binding " + std::to_string(bind) + " out of bounds";
845+
return sched_type_t::STATUS_ERROR_INVALID_PARAMETER;
846+
}
847+
}
848+
// It is common enough for every output to be passed (as part of general
849+
// code with a full set as a default value) that it is worth
850+
// detecting and ignoring in order to avoid hitting binding-handling
851+
// code and save time in initial placement and runqueue code.
852+
if (modifiers.output_binding.size() < static_cast<size_t>(output_count)) {
853+
input.binding = modifiers.output_binding;
854+
}
841855
input.priority = modifiers.priority;
842856
for (size_t i = 0; i < modifiers.regions_of_interest.size(); ++i) {
843857
const auto &range = modifiers.regions_of_interest[i];

clients/drcachesim/tests/scheduler_unit_tests.cpp

+64-2
Original file line numberDiff line numberDiff line change
@@ -2187,13 +2187,69 @@ test_synthetic_with_bindings_weighted()
21872187
assert(sched_as_string[4] == ".AA.AA.AA.AA.A.______________________________");
21882188
}
21892189

2190+
static void
2191+
test_synthetic_with_bindings_invalid()
2192+
{
2193+
std::cerr << "\n----------------\nTesting synthetic with invalid bindings\n";
2194+
static constexpr memref_tid_t TID_A = 42;
2195+
std::vector<trace_entry_t> refs_A = {
2196+
/* clang-format off */
2197+
make_thread(TID_A),
2198+
make_pid(1),
2199+
make_version(TRACE_ENTRY_VERSION),
2200+
make_timestamp(1),
2201+
make_instr(10),
2202+
make_exit(TID_A),
2203+
/* clang-format on */
2204+
};
2205+
{
2206+
// Test negative bindings.
2207+
static constexpr int NUM_OUTPUTS = 2;
2208+
std::vector<scheduler_t::input_reader_t> readers;
2209+
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_A)),
2210+
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_A);
2211+
std::vector<scheduler_t::input_workload_t> sched_inputs;
2212+
sched_inputs.emplace_back(std::move(readers));
2213+
std::set<scheduler_t::output_ordinal_t> cores;
2214+
cores.insert({ 1, -1 });
2215+
sched_inputs.back().thread_modifiers.emplace_back(cores);
2216+
scheduler_t::scheduler_options_t sched_ops(scheduler_t::MAP_TO_ANY_OUTPUT,
2217+
scheduler_t::DEPENDENCY_TIMESTAMPS,
2218+
scheduler_t::SCHEDULER_DEFAULTS,
2219+
/*verbosity=*/3);
2220+
scheduler_t scheduler;
2221+
assert(scheduler.init(sched_inputs, NUM_OUTPUTS, std::move(sched_ops)) ==
2222+
scheduler_t::STATUS_ERROR_INVALID_PARAMETER);
2223+
}
2224+
{
2225+
// Test too-large bindings.
2226+
static constexpr int NUM_OUTPUTS = 2;
2227+
std::vector<scheduler_t::input_reader_t> readers;
2228+
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_A)),
2229+
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_A);
2230+
std::vector<scheduler_t::input_workload_t> sched_inputs;
2231+
sched_inputs.emplace_back(std::move(readers));
2232+
std::set<scheduler_t::output_ordinal_t> cores;
2233+
cores.insert({ 1, 3 });
2234+
sched_inputs.back().thread_modifiers.emplace_back(cores);
2235+
scheduler_t::scheduler_options_t sched_ops(scheduler_t::MAP_TO_ANY_OUTPUT,
2236+
scheduler_t::DEPENDENCY_TIMESTAMPS,
2237+
scheduler_t::SCHEDULER_DEFAULTS,
2238+
/*verbosity=*/3);
2239+
scheduler_t scheduler;
2240+
assert(scheduler.init(sched_inputs, NUM_OUTPUTS, std::move(sched_ops)) ==
2241+
scheduler_t::STATUS_ERROR_INVALID_PARAMETER);
2242+
}
2243+
}
2244+
21902245
static void
21912246
test_synthetic_with_bindings()
21922247
{
21932248
test_synthetic_with_bindings_time(/*time_deps=*/true);
21942249
test_synthetic_with_bindings_time(/*time_deps=*/false);
21952250
test_synthetic_with_bindings_more_out();
21962251
test_synthetic_with_bindings_weighted();
2252+
test_synthetic_with_bindings_invalid();
21972253
}
21982254

21992255
static void
@@ -5676,13 +5732,14 @@ test_unscheduled_initially_rebalance()
56765732
// We need the initial runqueue assignment to be unbalanced.
56775733
// We achieve that by using input bindings.
56785734
// This relies on knowing the scheduler takes the 1st binding if there
5679-
// are any: so we can set to all cores and these will all pile up on output #0
5735+
// are any if the bindings don't include all cores: so we can set to all-but-one
5736+
// core and these will all pile up on output #0
56805737
// prior to the init-time rebalance. That makes output
56815738
// #0 big enough for a rebalance attempt, which causes scheduler init to fail
56825739
// without the i#7318 fix as it can only move one of those blocked inputs and
56835740
// so it hits an IDLE status on a later move attempt.
56845741
std::set<scheduler_t::output_ordinal_t> cores;
5685-
cores.insert({ 0, 1, 2 });
5742+
cores.insert({ 0, 1 });
56865743
std::vector<scheduler_t::input_workload_t> sched_inputs;
56875744
sched_inputs.emplace_back(std::move(readers));
56885745
sched_inputs.back().thread_modifiers.emplace_back(cores);
@@ -7026,6 +7083,11 @@ test_main(int argc, const char *argv[])
70267083
// Avoid races with lazy drdecode init (b/279350357).
70277084
dr_standalone_init();
70287085

7086+
if (argc < 20) {
7087+
test_synthetic_with_bindings_invalid();
7088+
test_unscheduled_initially_rebalance();
7089+
return 0;
7090+
}
70297091
test_serial();
70307092
test_parallel();
70317093
test_param_checks();

0 commit comments

Comments
 (0)