From 2cf4f22e8635b2c7cde8a5b7982d2762aa527d8b Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Tue, 27 Aug 2024 13:21:20 -0400 Subject: [PATCH 1/4] i#6938 sched migrate: Add scheduler statistics Adds schedule statistics to memtrace_stream.h. Implements these statistics in the streams returned by scheduler_t. This initial round includes total switches, time preempts, and direct switch attempts and successes. Adds checks that these match the schedule_stats tool's values. Adds tests of the values to several key scheduler unit tests. Issue: #6938 --- clients/drcachesim/common/memtrace_stream.h | 31 +++++++++++ clients/drcachesim/scheduler/scheduler.cpp | 34 ++++++++++++ clients/drcachesim/scheduler/scheduler.h | 20 ++++++- .../drcachesim/tests/scheduler_unit_tests.cpp | 53 ++++++++++++++++++- clients/drcachesim/tools/schedule_stats.cpp | 19 +++++++ 5 files changed, 155 insertions(+), 2 deletions(-) diff --git a/clients/drcachesim/common/memtrace_stream.h b/clients/drcachesim/common/memtrace_stream.h index 23e4d3af274..c0e27cb5b7a 100644 --- a/clients/drcachesim/common/memtrace_stream.h +++ b/clients/drcachesim/common/memtrace_stream.h @@ -64,6 +64,26 @@ namespace drmemtrace { /**< DrMemtrace tracing + simulation infrastructure names */ class memtrace_stream_t { public: + /** + * Statistics on the resulting schedule from interleaving and switching + * between the inputs. + */ + enum schedule_statistic_t { + /** Count of switches between inputs. */ + SCHED_STAT_SWITCHES, + /** + * Count of preempts due to time quantum expiration. Includes instances + * of the quantum expiring but no switch happening. + */ + SCHED_STAT_TIME_PREEMPTS, + /** Count of #TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH markers. */ + SCHED_STAT_DIRECT_SWITCH_ATTEMPTS, + /** Count of #TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH attempts that succeeded. */ + SCHED_STAT_DIRECT_SWITCH_SUCCESSES, + /** Count of statistic types. */ + SCHED_STAT_TYPE_COUNT, + }; + /** Destructor. */ virtual ~memtrace_stream_t() { @@ -240,6 +260,17 @@ class memtrace_stream_t { { return false; } + + /** + * Returns the value of the specified statistic for this output stream. + * The values for all output stream must be summed to obtain global counts. + * Returns -1 if statistics are not supported for this stream. + */ + virtual int64_t + get_schedule_statistic(schedule_statistic_t stat) const + { + return -1; + } }; /** diff --git a/clients/drcachesim/scheduler/scheduler.cpp b/clients/drcachesim/scheduler/scheduler.cpp index 69963ae6e47..e12c39bf524 100644 --- a/clients/drcachesim/scheduler/scheduler.cpp +++ b/clients/drcachesim/scheduler/scheduler.cpp @@ -664,6 +664,22 @@ scheduler_tmpl_t::stream_t::set_active(bool active) * Scheduler. */ +template +scheduler_tmpl_t::~scheduler_tmpl_t() +{ + for (unsigned int i = 0; i < outputs_.size(); ++i) { + VPRINT(this, 1, "Stats for output #%d\n", i); + VPRINT(this, 1, "%-25s: %9" PRId64 "\n", "Switches", + outputs_[i].stats[memtrace_stream_t::SCHED_STAT_SWITCHES]); + VPRINT(this, 1, "%-25s: %9" PRId64 "\n", "Time preempts", + outputs_[i].stats[memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS]); + VPRINT(this, 1, "%-25s: %9" PRId64 "\n", "Direct switch attempts", + outputs_[i].stats[memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_ATTEMPTS]); + VPRINT(this, 1, "%-25s: %9" PRId64 "\n", "Direct switch successes", + outputs_[i].stats[memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_SUCCESSES]); + } +} + template bool scheduler_tmpl_t::check_valid_input_limits( @@ -2595,6 +2611,7 @@ scheduler_tmpl_t::set_cur_input(output_ordinal_t output, if (outputs_[output].prev_input >= 0) { std::lock_guard lock(*inputs_[outputs_[output].prev_input].lock); prev_workload = inputs_[outputs_[output].prev_input].workload; + ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCHES]; } std::lock_guard lock(*inputs_[input].lock); @@ -2895,6 +2912,8 @@ scheduler_tmpl_t::pick_next_input(output_ordinal_t outpu target->blocked_time = 0; target->unscheduled = false; } + ++outputs_[output].stats + [memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_SUCCESSES]; } else if (unscheduled_priority_.find(target)) { target->unscheduled = false; unscheduled_priority_.erase(target); @@ -2905,6 +2924,8 @@ scheduler_tmpl_t::pick_next_input(output_ordinal_t outpu "@%" PRIu64 "\n", output, prev_index, target->index, inputs_[prev_index].reader->get_last_timestamp()); + ++outputs_[output].stats + [memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_SUCCESSES]; } else { // We assume that inter-input dependencies are captured in // the _DIRECT_THREAD_SWITCH, _UNSCHEDULE, and _SCHEDULE markers @@ -3056,6 +3077,7 @@ scheduler_tmpl_t::process_marker(input_info_t &input, case TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH: { if (!options_.honor_direct_switches) break; + ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_ATTEMPTS]; memref_tid_t target_tid = marker_value; auto it = tid2input_.find(workload_tid_t(input.workload, target_tid)); if (it == tid2input_.end()) { @@ -3404,6 +3426,7 @@ scheduler_tmpl_t::next_record(output_ordinal_t output, preempt = !need_new_input; need_new_input = true; input->instrs_in_quantum = 0; + ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS]; } } else if (options_.quantum_unit == QUANTUM_TIME) { if (cur_time == 0 || cur_time < input->prev_time_in_quantum) { @@ -3427,6 +3450,7 @@ scheduler_tmpl_t::next_record(output_ordinal_t output, preempt = !need_new_input; need_new_input = true; input->time_spent_in_quantum = 0; + ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS]; } } } @@ -3687,6 +3711,16 @@ scheduler_tmpl_t::is_record_kernel(output_ordinal_t outp return inputs_[index].reader->is_record_kernel(); } +template +int64_t +scheduler_tmpl_t::get_statistic( + output_ordinal_t output, memtrace_stream_t::schedule_statistic_t stat) const +{ + if (stat >= memtrace_stream_t::SCHED_STAT_TYPE_COUNT) + return -1; + return outputs_[output].stats[stat]; +} + template typename scheduler_tmpl_t::stream_status_t scheduler_tmpl_t::set_output_active(output_ordinal_t output, diff --git a/clients/drcachesim/scheduler/scheduler.h b/clients/drcachesim/scheduler/scheduler.h index 5f3b2516461..6c8a426409a 100644 --- a/clients/drcachesim/scheduler/scheduler.h +++ b/clients/drcachesim/scheduler/scheduler.h @@ -1125,6 +1125,16 @@ template class scheduler_tmpl_t { return scheduler_->is_record_kernel(ordinal_); } + /** + * Returns the value of the specified statistic for this output stream. + * The values for all output stream must be summed to obtain global counts. + */ + int64_t + get_schedule_statistic(schedule_statistic_t stat) const override + { + return scheduler_->get_statistic(ordinal_, stat); + } + protected: scheduler_tmpl_t *scheduler_ = nullptr; int ordinal_ = -1; @@ -1153,7 +1163,7 @@ template class scheduler_tmpl_t { : ready_priority_(static_cast(get_time_micros())) { } - virtual ~scheduler_tmpl_t() = default; + virtual ~scheduler_tmpl_t(); /** * Initializes the scheduler for the given inputs, count of output streams, and @@ -1440,6 +1450,9 @@ template class scheduler_tmpl_t { bool at_eof = false; // Used for replaying wait periods. uint64_t wait_start_time = 0; + // Exported statistics. + std::vector stats = + std::vector(memtrace_stream_t::SCHED_STAT_TYPE_COUNT); }; // Used for reading as-traced schedules. @@ -1791,6 +1804,11 @@ template class scheduler_tmpl_t { // to kernel execution. bool is_record_kernel(output_ordinal_t output); + + int64_t + get_statistic(output_ordinal_t output, + memtrace_stream_t::schedule_statistic_t stat) const; + /////////////////////////////////////////////////////////////////////////// // Support for ready queues for who to schedule next: diff --git a/clients/drcachesim/tests/scheduler_unit_tests.cpp b/clients/drcachesim/tests/scheduler_unit_tests.cpp index 303393e4efb..ae344504d39 100644 --- a/clients/drcachesim/tests/scheduler_unit_tests.cpp +++ b/clients/drcachesim/tests/scheduler_unit_tests.cpp @@ -130,6 +130,21 @@ memref_is_nop_instr(memref_t &record) return pc != nullptr && instr_is_nop(instr); } +static void +verify_scheduler_stats(scheduler_t::stream_t *stream, int64_t switches, int64_t preempts, + int64_t direct_attempts, int64_t direct_successes) +{ + assert(stream->get_schedule_statistic(memtrace_stream_t::SCHED_STAT_SWITCHES) == + switches); + assert(stream->get_schedule_statistic(memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS) == + preempts); + assert(stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_ATTEMPTS) == direct_attempts); + assert(stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_SUCCESSES) == + direct_successes); +} + static void test_serial() { @@ -1076,6 +1091,13 @@ test_synthetic() for (int i = 0; i < NUM_OUTPUTS; i++) { std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n"; } + // Check scheduler stats. # switches is the # of letter transitions; # preempts + // is the count of 3-letters-in-a-row sequences (3 is the quantum) except the + // last for an input (EOF doesn't count as a preempt). + verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/10, /*preempts=*/6, + /*direct_attempts=*/0, /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/11, /*preempts=*/8, + /*direct_attempts=*/0, /*direct_successes=*/0); #ifndef WIN32 // XXX: Windows microseconds on test VMs are very coarse and stay the same // for long periods. Instruction quanta use wall-clock idle times, so @@ -1256,6 +1278,11 @@ test_synthetic_time_quanta() check_next(cpu0, ++time, scheduler_t::STATUS_EOF); if (scheduler.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS) assert(false); + // Check scheduler stats. + verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/2, /*preempts=*/2, + /*direct_attempts=*/0, /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/3, /*preempts=*/1, + /*direct_attempts=*/0, /*direct_successes=*/0); } { replay_file_checker_t checker; @@ -1377,6 +1404,13 @@ test_synthetic_with_timestamps() ".CC.C.II.IC.CC.F.FF.I.II.FF.F..BB.B.HH.HE.EE.BB.B.HH.H..DD.DA.AA.G.GG.DD.D._"); assert(sched_as_string[1] == ".FF.F.JJ.JJ.JJ.JJ.J.CC.C.II.I..EE.EB.BB.H.HH.EE.E..AA.A.GG.GD.DD.AA.A.GG.G."); + // Check scheduler stats. # switches is the # of letter transitions; # preempts + // is the count of 3-letters-in-a-row sequences (3 is the quantum) except the + // last for an input (EOF doesn't count as a preempt). + verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/14, /*preempts=*/11, + /*direct_attempts=*/0, /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/14, /*preempts=*/9, + /*direct_attempts=*/0, /*direct_successes=*/0); } static void @@ -1461,6 +1495,13 @@ test_synthetic_with_priorities() ".BB.B.HH.HE.EE.BB.B.HH.H..FF.F.JJ.JJ.JJ.JJ.J.CC.C.II.I..DD.DA.AA.G.GG.DD.D._"); assert(sched_as_string[1] == ".EE.EB.BB.H.HH.EE.E..CC.C.II.IC.CC.F.FF.I.II.FF.F..AA.A.GG.GD.DD.AA.A.GG.G."); + // Check scheduler stats. # switches is the # of letter transitions; # preempts + // is the count of 3-letters-in-a-row sequences (3 is the quantum) except the + // last for an input (EOF doesn't count as a preempt). + verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/14, /*preempts=*/9, + /*direct_attempts=*/0, /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/14, /*preempts=*/11, + /*direct_attempts=*/0, /*direct_successes=*/0); } static void @@ -1781,6 +1822,13 @@ test_synthetic_with_syscalls_multiple() assert(sched_as_string[0] == "BHHHFFFJJJJJJBEEHHHFFFBCCCEEIIIDDDBAAAGGGDDDB________B_______"); assert(sched_as_string[1] == "EECCCIIICCCBEEJJJHHHIIIFFFEAAAGGGBDDDAAAGGG________B"); + // Check scheduler stats. # switches is the # of letter transitions; # preempts + // is the count of 3-letters-in-a-row sequences (3 is the quantum) except the + // last for an input (EOF doesn't count as a preempt). + verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/20, /*preempts=*/11, + /*direct_attempts=*/0, /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/17, /*preempts=*/10, + /*direct_attempts=*/0, /*direct_successes=*/0); } static void @@ -4173,6 +4221,8 @@ test_direct_switch() std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n"; } assert(sched_as_string[0] == CORE0_SCHED_STRING); + verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/4, /*preempts=*/0, + /*direct_attempts=*/3, /*direct_successes=*/2); } { // Test disabling direct switches. @@ -4210,6 +4260,8 @@ test_direct_switch() std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n"; } assert(sched_as_string[0] == CORE0_SCHED_STRING); + verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/4, /*preempts=*/0, + /*direct_attempts=*/0, /*direct_successes=*/0); } } @@ -5353,7 +5405,6 @@ test_main(int argc, const char *argv[]) test_kernel_switch_sequences(); test_random_schedule(); test_record_scheduler(); - dr_standalone_exit(); return 0; } diff --git a/clients/drcachesim/tools/schedule_stats.cpp b/clients/drcachesim/tools/schedule_stats.cpp index 47637430bbe..ca7d335a434 100644 --- a/clients/drcachesim/tools/schedule_stats.cpp +++ b/clients/drcachesim/tools/schedule_stats.cpp @@ -411,6 +411,25 @@ schedule_stats_t::aggregate_results(counters_t &total) { for (const auto &shard : shard_map_) { total += shard.second->counters; + // Sanity check against the scheduler's own stats, unless the trace + // is pre-scheduled. + if (TESTANY(OFFLINE_FILE_TYPE_CORE_SHARDED, shard.second->filetype)) + continue; + assert(shard.second->counters.total_switches == + shard.second->stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_SWITCHES)); + // We can only find a lower bound on preempts as the preempt stat from + // the scheduler includes no-switch cases. + assert(shard.second->counters.total_switches - + shard.second->counters.voluntary_switches <= + shard.second->stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS)); + assert(shard.second->counters.direct_switch_requests == + shard.second->stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_ATTEMPTS)); + assert(shard.second->counters.direct_switches == + shard.second->stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_SUCCESSES)); } } From 079f27165c0ce2df7ae0201b569d449f1f84688c Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Tue, 27 Aug 2024 15:26:08 -0400 Subject: [PATCH 2/4] Disable stat-vs-tool asserts in core-serial where there is no access to the separate streams --- clients/drcachesim/tools/schedule_stats.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clients/drcachesim/tools/schedule_stats.cpp b/clients/drcachesim/tools/schedule_stats.cpp index ca7d335a434..c15afec3c3f 100644 --- a/clients/drcachesim/tools/schedule_stats.cpp +++ b/clients/drcachesim/tools/schedule_stats.cpp @@ -412,8 +412,10 @@ schedule_stats_t::aggregate_results(counters_t &total) for (const auto &shard : shard_map_) { total += shard.second->counters; // Sanity check against the scheduler's own stats, unless the trace - // is pre-scheduled. - if (TESTANY(OFFLINE_FILE_TYPE_CORE_SHARDED, shard.second->filetype)) + // is pre-scheduled or we're in core-serial mode where we don't have access + // to the separate output streams. + if (TESTANY(OFFLINE_FILE_TYPE_CORE_SHARDED, shard.second->filetype) || + serial_stream_ != nullptr) continue; assert(shard.second->counters.total_switches == shard.second->stream->get_schedule_statistic( From eaee39ef974ece46cf77b6592816ecfa6cfae8dd Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Wed, 28 Aug 2024 16:55:18 -0400 Subject: [PATCH 3/4] Split switch count into 4 categories; return double; s/time/quantum/ --- clients/drcachesim/common/memtrace_stream.h | 27 ++-- clients/drcachesim/scheduler/scheduler.cpp | 58 ++++++--- clients/drcachesim/scheduler/scheduler.h | 9 +- .../drcachesim/tests/scheduler_unit_tests.cpp | 117 ++++++++++++------ clients/drcachesim/tools/schedule_stats.cpp | 15 ++- 5 files changed, 153 insertions(+), 73 deletions(-) diff --git a/clients/drcachesim/common/memtrace_stream.h b/clients/drcachesim/common/memtrace_stream.h index c0e27cb5b7a..e18f534ddb0 100644 --- a/clients/drcachesim/common/memtrace_stream.h +++ b/clients/drcachesim/common/memtrace_stream.h @@ -66,16 +66,29 @@ class memtrace_stream_t { public: /** * Statistics on the resulting schedule from interleaving and switching - * between the inputs. + * between the inputs in core-sharded modes. */ enum schedule_statistic_t { - /** Count of switches between inputs. */ - SCHED_STAT_SWITCHES, + /** Count of context switches away from an input to a different input. */ + SCHED_STAT_SWITCH_INPUT_TO_INPUT, + /** Count of context switches away from an input to an idle state. */ + SCHED_STAT_SWITCH_INPUT_TO_IDLE, /** - * Count of preempts due to time quantum expiration. Includes instances - * of the quantum expiring but no switch happening. + * Count of context switches away from idle to an input. + * This does not include the initial assignment of an input to a core. */ - SCHED_STAT_TIME_PREEMPTS, + SCHED_STAT_SWITCH_IDLE_TO_INPUT, + /** + * Count of quantum preempt points where the same input remains in place + * as nothing else of equal or greater priority is available. + */ + SCHED_STAT_SWITCH_NOP, + /** + * Count of preempts due to quantum expiration. Includes instances + * of the quantum expiring but no switch happening (but #SCHED_STAT_SWITCH_NOP + * can be used to separate those). + */ + SCHED_STAT_QUANTUM_PREEMPTS, /** Count of #TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH markers. */ SCHED_STAT_DIRECT_SWITCH_ATTEMPTS, /** Count of #TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH attempts that succeeded. */ @@ -266,7 +279,7 @@ class memtrace_stream_t { * The values for all output stream must be summed to obtain global counts. * Returns -1 if statistics are not supported for this stream. */ - virtual int64_t + virtual double get_schedule_statistic(schedule_statistic_t stat) const { return -1; diff --git a/clients/drcachesim/scheduler/scheduler.cpp b/clients/drcachesim/scheduler/scheduler.cpp index e12c39bf524..96586824a8c 100644 --- a/clients/drcachesim/scheduler/scheduler.cpp +++ b/clients/drcachesim/scheduler/scheduler.cpp @@ -669,13 +669,19 @@ scheduler_tmpl_t::~scheduler_tmpl_t() { for (unsigned int i = 0; i < outputs_.size(); ++i) { VPRINT(this, 1, "Stats for output #%d\n", i); - VPRINT(this, 1, "%-25s: %9" PRId64 "\n", "Switches", - outputs_[i].stats[memtrace_stream_t::SCHED_STAT_SWITCHES]); - VPRINT(this, 1, "%-25s: %9" PRId64 "\n", "Time preempts", - outputs_[i].stats[memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS]); - VPRINT(this, 1, "%-25s: %9" PRId64 "\n", "Direct switch attempts", + VPRINT(this, 1, " %-25s: %9" PRId64 "\n", "Switch input->input", + outputs_[i].stats[memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_INPUT]); + VPRINT(this, 1, " %-25s: %9" PRId64 "\n", "Switch input->idle", + outputs_[i].stats[memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_IDLE]); + VPRINT(this, 1, " %-25s: %9" PRId64 "\n", "Switch idle->input", + outputs_[i].stats[memtrace_stream_t::SCHED_STAT_SWITCH_IDLE_TO_INPUT]); + VPRINT(this, 1, " %-25s: %9" PRId64 "\n", "Switch nop", + outputs_[i].stats[memtrace_stream_t::SCHED_STAT_SWITCH_NOP]); + VPRINT(this, 1, " %-25s: %9" PRId64 "\n", "Quantum preempts", + outputs_[i].stats[memtrace_stream_t::SCHED_STAT_QUANTUM_PREEMPTS]); + VPRINT(this, 1, " %-25s: %9" PRId64 "\n", "Direct switch attempts", outputs_[i].stats[memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_ATTEMPTS]); - VPRINT(this, 1, "%-25s: %9" PRId64 "\n", "Direct switch successes", + VPRINT(this, 1, " %-25s: %9" PRId64 "\n", "Direct switch successes", outputs_[i].stats[memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_SUCCESSES]); } } @@ -2133,7 +2139,7 @@ scheduler_tmpl_t::advance_region_of_interest( input.cur_region); if (input.cur_region >= static_cast(input.regions_of_interest.size())) { if (input.at_eof) - return eof_or_idle(output, /*hold_sched_lock=*/false); + return eof_or_idle(output, /*hold_sched_lock=*/false, input.index); else { // We let the user know we're done. if (options_.schedule_record_ostream != nullptr) { @@ -2608,10 +2614,9 @@ scheduler_tmpl_t::set_cur_input(output_ordinal_t output, return STATUS_OK; int prev_workload = -1; - if (outputs_[output].prev_input >= 0) { + if (outputs_[output].prev_input >= 0 && outputs_[output].prev_input != input) { std::lock_guard lock(*inputs_[outputs_[output].prev_input].lock); prev_workload = inputs_[outputs_[output].prev_input].workload; - ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCHES]; } std::lock_guard lock(*inputs_[input].lock); @@ -2698,7 +2703,7 @@ scheduler_tmpl_t::pick_next_input_as_previously( outputs_[output].at_eof = true; live_replay_output_count_.fetch_add(-1, std::memory_order_release); } - return eof_or_idle(output, need_sched_lock()); + return eof_or_idle(output, need_sched_lock(), outputs_[output].cur_input); } const schedule_record_t &segment = outputs_[output].record[outputs_[output].record_index + 1]; @@ -2949,11 +2954,11 @@ scheduler_tmpl_t::pick_next_input(output_ordinal_t outpu // We found a direct switch target above. } else if (ready_queue_empty() && blocked_time == 0) { if (prev_index == INVALID_INPUT_ORDINAL) - return eof_or_idle(output, need_lock); + return eof_or_idle(output, need_lock, prev_index); auto lock = std::unique_lock(*inputs_[prev_index].lock); if (inputs_[prev_index].at_eof) { lock.unlock(); - return eof_or_idle(output, need_lock); + return eof_or_idle(output, need_lock, prev_index); } else index = prev_index; // Go back to prior. } else { @@ -2981,7 +2986,7 @@ scheduler_tmpl_t::pick_next_input(output_ordinal_t outpu } if (queue_next == nullptr) { assert(blocked_time == 0 || prev_index == INVALID_INPUT_ORDINAL); - return eof_or_idle(output, need_lock); + return eof_or_idle(output, need_lock, prev_index); } index = queue_next->index; } @@ -2996,7 +3001,7 @@ scheduler_tmpl_t::pick_next_input(output_ordinal_t outpu } } if (index < 0) - return eof_or_idle(output, need_lock); + return eof_or_idle(output, need_lock, prev_index); VPRINT(this, 2, "next_record[%d]: advancing to timestamp %" PRIu64 " == input #%d\n", @@ -3038,6 +3043,16 @@ scheduler_tmpl_t::pick_next_input(output_ordinal_t outpu } break; } + // We can't easily place these stats inside set_cur_input() as we call that to + // temporarily give up our input. + if (prev_index == index) + ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_NOP]; + else if (prev_index != INVALID_INPUT_ORDINAL && index != INVALID_INPUT_ORDINAL) + ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_INPUT]; + else if (index == INVALID_INPUT_ORDINAL) + ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_IDLE]; + else + ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_IDLE_TO_INPUT]; set_cur_input(output, index); return res; } @@ -3235,7 +3250,7 @@ scheduler_tmpl_t::next_record(output_ordinal_t output, if (outputs_[output].cur_input < 0) { // This happens with more outputs than inputs. For non-empty outputs we // require cur_input to be set to >=0 during init(). - return eof_or_idle(output, /*hold_sched_lock=*/false); + return eof_or_idle(output, /*hold_sched_lock=*/false, outputs_[output].cur_input); } input = &inputs_[outputs_[output].cur_input]; auto lock = std::unique_lock(*input->lock); @@ -3426,7 +3441,8 @@ scheduler_tmpl_t::next_record(output_ordinal_t output, preempt = !need_new_input; need_new_input = true; input->instrs_in_quantum = 0; - ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS]; + ++outputs_[output] + .stats[memtrace_stream_t::SCHED_STAT_QUANTUM_PREEMPTS]; } } else if (options_.quantum_unit == QUANTUM_TIME) { if (cur_time == 0 || cur_time < input->prev_time_in_quantum) { @@ -3450,7 +3466,8 @@ scheduler_tmpl_t::next_record(output_ordinal_t output, preempt = !need_new_input; need_new_input = true; input->time_spent_in_quantum = 0; - ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS]; + ++outputs_[output] + .stats[memtrace_stream_t::SCHED_STAT_QUANTUM_PREEMPTS]; } } } @@ -3637,7 +3654,8 @@ scheduler_tmpl_t::mark_input_eof(input_info_t &input) template typename scheduler_tmpl_t::stream_status_t scheduler_tmpl_t::eof_or_idle(output_ordinal_t output, - bool hold_sched_lock) + bool hold_sched_lock, + input_ordinal_t prev_input) { // XXX i#6831: Refactor to use subclasses or templates to specialize // scheduler code based on mapping options, to avoid these top-level @@ -3696,6 +3714,8 @@ scheduler_tmpl_t::eof_or_idle(output_ordinal_t output, } } outputs_[output].waiting = true; + if (prev_input > 0) + ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_IDLE]; set_cur_input(output, INVALID_INPUT_ORDINAL); return sched_type_t::STATUS_IDLE; } @@ -3712,7 +3732,7 @@ scheduler_tmpl_t::is_record_kernel(output_ordinal_t outp } template -int64_t +double scheduler_tmpl_t::get_statistic( output_ordinal_t output, memtrace_stream_t::schedule_statistic_t stat) const { diff --git a/clients/drcachesim/scheduler/scheduler.h b/clients/drcachesim/scheduler/scheduler.h index 6c8a426409a..14dfd64da09 100644 --- a/clients/drcachesim/scheduler/scheduler.h +++ b/clients/drcachesim/scheduler/scheduler.h @@ -1129,7 +1129,7 @@ template class scheduler_tmpl_t { * Returns the value of the specified statistic for this output stream. * The values for all output stream must be summed to obtain global counts. */ - int64_t + double get_schedule_statistic(schedule_statistic_t stat) const override { return scheduler_->get_statistic(ordinal_, stat); @@ -1450,7 +1450,7 @@ template class scheduler_tmpl_t { bool at_eof = false; // Used for replaying wait periods. uint64_t wait_start_time = 0; - // Exported statistics. + // Exported statistics. Currently all integers and cast to double on export. std::vector stats = std::vector(memtrace_stream_t::SCHED_STAT_TYPE_COUNT); }; @@ -1797,7 +1797,8 @@ template class scheduler_tmpl_t { // Determines whether to exit or wait for other outputs when one output // runs out of things to do. May end up scheduling new inputs. stream_status_t - eof_or_idle(output_ordinal_t output, bool hold_sched_lock); + eof_or_idle(output_ordinal_t output, bool hold_sched_lock, + input_ordinal_t prev_input); // Returns whether the current record for the current input stream scheduled on // the 'output_ordinal'-th output stream is from a part of the trace corresponding @@ -1805,7 +1806,7 @@ template class scheduler_tmpl_t { bool is_record_kernel(output_ordinal_t output); - int64_t + double get_statistic(output_ordinal_t output, memtrace_stream_t::schedule_statistic_t stat) const; diff --git a/clients/drcachesim/tests/scheduler_unit_tests.cpp b/clients/drcachesim/tests/scheduler_unit_tests.cpp index ae344504d39..4b26318ee14 100644 --- a/clients/drcachesim/tests/scheduler_unit_tests.cpp +++ b/clients/drcachesim/tests/scheduler_unit_tests.cpp @@ -131,13 +131,26 @@ memref_is_nop_instr(memref_t &record) } static void -verify_scheduler_stats(scheduler_t::stream_t *stream, int64_t switches, int64_t preempts, - int64_t direct_attempts, int64_t direct_successes) +verify_scheduler_stats(scheduler_t::stream_t *stream, int64_t switch_input_to_input, + int64_t switch_input_to_idle, int64_t switch_idle_to_input, + int64_t switch_nop, int64_t preempts, int64_t direct_attempts, + int64_t direct_successes) { - assert(stream->get_schedule_statistic(memtrace_stream_t::SCHED_STAT_SWITCHES) == - switches); - assert(stream->get_schedule_statistic(memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS) == - preempts); + // We assume our counts fit in the get_schedule_statistic()'s double's 54-bit + // mantissa and thus we can safely use "==". + assert(stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_INPUT) == + switch_input_to_input); + assert(stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_IDLE) == + switch_input_to_idle); + assert(stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_SWITCH_IDLE_TO_INPUT) == + switch_idle_to_input); + assert(stream->get_schedule_statistic(memtrace_stream_t::SCHED_STAT_SWITCH_NOP) == + switch_nop); + assert(stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_QUANTUM_PREEMPTS) == preempts); assert(stream->get_schedule_statistic( memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_ATTEMPTS) == direct_attempts); assert(stream->get_schedule_statistic( @@ -1092,12 +1105,17 @@ test_synthetic() std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n"; } // Check scheduler stats. # switches is the # of letter transitions; # preempts - // is the count of 3-letters-in-a-row sequences (3 is the quantum) except the - // last for an input (EOF doesn't count as a preempt). - verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/10, /*preempts=*/6, - /*direct_attempts=*/0, /*direct_successes=*/0); - verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/11, /*preempts=*/8, - /*direct_attempts=*/0, /*direct_successes=*/0); + // is the instances where the same letter appears 3 times without another letter + // appearing in between (and ignoring the last letter for an input: EOF doesn't + // count as a preempt). + verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/10, + /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/0, + /*switch_nop=*/0, /*preempts=*/6, /*direct_attempts=*/0, + /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switch_input_to_input=*/11, + /*switch_input_to_idle=*/1, /*switch_idle_to_input=*/0, + /*switch_nop=*/0, /*preempts=*/8, /*direct_attempts=*/0, + /*direct_successes=*/0); #ifndef WIN32 // XXX: Windows microseconds on test VMs are very coarse and stay the same // for long periods. Instruction quanta use wall-clock idle times, so @@ -1279,10 +1297,14 @@ test_synthetic_time_quanta() if (scheduler.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS) assert(false); // Check scheduler stats. - verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/2, /*preempts=*/2, - /*direct_attempts=*/0, /*direct_successes=*/0); - verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/3, /*preempts=*/1, - /*direct_attempts=*/0, /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/1, + /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/0, + /*switch_nop=*/1, /*preempts=*/2, /*direct_attempts=*/0, + /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switch_input_to_input=*/2, + /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/1, + /*switch_nop=*/0, /*preempts=*/1, /*direct_attempts=*/0, + /*direct_successes=*/0); } { replay_file_checker_t checker; @@ -1405,12 +1427,17 @@ test_synthetic_with_timestamps() assert(sched_as_string[1] == ".FF.F.JJ.JJ.JJ.JJ.J.CC.C.II.I..EE.EB.BB.H.HH.EE.E..AA.A.GG.GD.DD.AA.A.GG.G."); // Check scheduler stats. # switches is the # of letter transitions; # preempts - // is the count of 3-letters-in-a-row sequences (3 is the quantum) except the - // last for an input (EOF doesn't count as a preempt). - verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/14, /*preempts=*/11, - /*direct_attempts=*/0, /*direct_successes=*/0); - verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/14, /*preempts=*/9, - /*direct_attempts=*/0, /*direct_successes=*/0); + // is the instances where the same letter appears 3 times without another letter + // appearing in between (and ignoring the last letter for an input: EOF doesn't + // count as a preempt). + verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/14, + /*switch_input_to_idle=*/1, /*switch_idle_to_input=*/0, + /*switch_nop=*/0, /*preempts=*/11, /*direct_attempts=*/0, + /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switch_input_to_input=*/12, + /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/0, + /*switch_nop=*/2, /*preempts=*/9, /*direct_attempts=*/0, + /*direct_successes=*/0); } static void @@ -1496,12 +1523,17 @@ test_synthetic_with_priorities() assert(sched_as_string[1] == ".EE.EB.BB.H.HH.EE.E..CC.C.II.IC.CC.F.FF.I.II.FF.F..AA.A.GG.GD.DD.AA.A.GG.G."); // Check scheduler stats. # switches is the # of letter transitions; # preempts - // is the count of 3-letters-in-a-row sequences (3 is the quantum) except the - // last for an input (EOF doesn't count as a preempt). - verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/14, /*preempts=*/9, - /*direct_attempts=*/0, /*direct_successes=*/0); - verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/14, /*preempts=*/11, - /*direct_attempts=*/0, /*direct_successes=*/0); + // is the instances where the same letter appears 3 times without another letter + // appearing in between (and ignoring the last letter for an input: EOF doesn't + // count as a preempt). + verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/12, + /*switch_input_to_idle=*/1, /*switch_idle_to_input=*/0, + /*switch_nop=*/2, /*preempts=*/9, /*direct_attempts=*/0, + /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switch_input_to_input=*/14, + /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/0, + /*switch_nop=*/0, /*preempts=*/11, /*direct_attempts=*/0, + /*direct_successes=*/0); } static void @@ -1823,12 +1855,17 @@ test_synthetic_with_syscalls_multiple() "BHHHFFFJJJJJJBEEHHHFFFBCCCEEIIIDDDBAAAGGGDDDB________B_______"); assert(sched_as_string[1] == "EECCCIIICCCBEEJJJHHHIIIFFFEAAAGGGBDDDAAAGGG________B"); // Check scheduler stats. # switches is the # of letter transitions; # preempts - // is the count of 3-letters-in-a-row sequences (3 is the quantum) except the - // last for an input (EOF doesn't count as a preempt). - verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/20, /*preempts=*/11, - /*direct_attempts=*/0, /*direct_successes=*/0); - verify_scheduler_stats(scheduler.get_stream(1), /*switches=*/17, /*preempts=*/10, - /*direct_attempts=*/0, /*direct_successes=*/0); + // is the instances where the same letter appears 3 times without another letter + // appearing in between (and ignoring the last letter for an input: EOF doesn't + // count as a preempt). + verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/17, + /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/1, + /*switch_nop=*/2, /*preempts=*/11, /*direct_attempts=*/0, + /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(1), /*switch_input_to_input=*/16, + /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/1, + /*switch_nop=*/0, /*preempts=*/10, /*direct_attempts=*/0, + /*direct_successes=*/0); } static void @@ -4221,8 +4258,10 @@ test_direct_switch() std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n"; } assert(sched_as_string[0] == CORE0_SCHED_STRING); - verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/4, /*preempts=*/0, - /*direct_attempts=*/3, /*direct_successes=*/2); + verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/3, + /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/1, + /*switch_nop=*/0, /*preempts=*/0, /*direct_attempts=*/3, + /*direct_successes=*/2); } { // Test disabling direct switches. @@ -4260,8 +4299,10 @@ test_direct_switch() std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n"; } assert(sched_as_string[0] == CORE0_SCHED_STRING); - verify_scheduler_stats(scheduler.get_stream(0), /*switches=*/4, /*preempts=*/0, - /*direct_attempts=*/0, /*direct_successes=*/0); + verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/2, + /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/2, + /*switch_nop=*/0, /*preempts=*/0, /*direct_attempts=*/0, + /*direct_successes=*/0); } } diff --git a/clients/drcachesim/tools/schedule_stats.cpp b/clients/drcachesim/tools/schedule_stats.cpp index c15afec3c3f..4a0dc47ea91 100644 --- a/clients/drcachesim/tools/schedule_stats.cpp +++ b/clients/drcachesim/tools/schedule_stats.cpp @@ -417,15 +417,20 @@ schedule_stats_t::aggregate_results(counters_t &total) if (TESTANY(OFFLINE_FILE_TYPE_CORE_SHARDED, shard.second->filetype) || serial_stream_ != nullptr) continue; + // We assume our counts fit in the get_schedule_statistic()'s double's 54-bit + // mantissa and thus we can safely use "==". + // Currently our switch count ignores input-to-idle. assert(shard.second->counters.total_switches == shard.second->stream->get_schedule_statistic( - memtrace_stream_t::SCHED_STAT_SWITCHES)); - // We can only find a lower bound on preempts as the preempt stat from - // the scheduler includes no-switch cases. + memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_INPUT) + + shard.second->stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_SWITCH_IDLE_TO_INPUT)); assert(shard.second->counters.total_switches - - shard.second->counters.voluntary_switches <= + shard.second->counters.voluntary_switches == shard.second->stream->get_schedule_statistic( - memtrace_stream_t::SCHED_STAT_TIME_PREEMPTS)); + memtrace_stream_t::SCHED_STAT_QUANTUM_PREEMPTS) - + shard.second->stream->get_schedule_statistic( + memtrace_stream_t::SCHED_STAT_SWITCH_NOP)); assert(shard.second->counters.direct_switch_requests == shard.second->stream->get_schedule_statistic( memtrace_stream_t::SCHED_STAT_DIRECT_SWITCH_ATTEMPTS)); From 16b04c7a5e3169b7eec8fb2a32fb14555c6eac1b Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Wed, 28 Aug 2024 17:46:00 -0400 Subject: [PATCH 4/4] Add explicit cast to satisfy Windows warning; add missing input->idle case. --- clients/drcachesim/scheduler/scheduler.cpp | 9 +++++++-- clients/drcachesim/scheduler/scheduler.h | 6 +++++- clients/drcachesim/tests/scheduler_unit_tests.cpp | 12 ++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/clients/drcachesim/scheduler/scheduler.cpp b/clients/drcachesim/scheduler/scheduler.cpp index 55549292e43..d4ed82bc204 100644 --- a/clients/drcachesim/scheduler/scheduler.cpp +++ b/clients/drcachesim/scheduler/scheduler.cpp @@ -2981,6 +2981,11 @@ scheduler_tmpl_t::pick_next_input(output_ordinal_t outpu if (record_status != sched_type_t::STATUS_OK) return record_status; } + if (prev_index != INVALID_INPUT_ORDINAL) { + ++outputs_[output] + .stats[memtrace_stream_t:: + SCHED_STAT_SWITCH_INPUT_TO_IDLE]; + } } return status; } @@ -3714,7 +3719,7 @@ scheduler_tmpl_t::eof_or_idle(output_ordinal_t output, } } outputs_[output].waiting = true; - if (prev_input > 0) + if (prev_input != INVALID_INPUT_ORDINAL) ++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_IDLE]; set_cur_input(output, INVALID_INPUT_ORDINAL); return sched_type_t::STATUS_IDLE; @@ -3738,7 +3743,7 @@ scheduler_tmpl_t::get_statistic( { if (stat >= memtrace_stream_t::SCHED_STAT_TYPE_COUNT) return -1; - return outputs_[output].stats[stat]; + return static_cast(outputs_[output].stats[stat]); } template diff --git a/clients/drcachesim/scheduler/scheduler.h b/clients/drcachesim/scheduler/scheduler.h index c4b2da44c51..b5f4f2b23e9 100644 --- a/clients/drcachesim/scheduler/scheduler.h +++ b/clients/drcachesim/scheduler/scheduler.h @@ -1131,7 +1131,9 @@ template class scheduler_tmpl_t { /** * Returns the value of the specified statistic for this output stream. - * The values for all output stream must be summed to obtain global counts. + * The values for all output streams must be summed to obtain global counts. + * These statistics are not guaranteed to be accurate when replaying a + * prior schedule via #MAP_TO_RECORDED_OUTPUT. */ double get_schedule_statistic(schedule_statistic_t stat) const override @@ -1810,6 +1812,8 @@ template class scheduler_tmpl_t { bool is_record_kernel(output_ordinal_t output); + // These statistics are not guaranteed to be accurate when replaying a + // prior schedule. double get_statistic(output_ordinal_t output, memtrace_stream_t::schedule_statistic_t stat) const; diff --git a/clients/drcachesim/tests/scheduler_unit_tests.cpp b/clients/drcachesim/tests/scheduler_unit_tests.cpp index 4b26318ee14..bca345d4bc3 100644 --- a/clients/drcachesim/tests/scheduler_unit_tests.cpp +++ b/clients/drcachesim/tests/scheduler_unit_tests.cpp @@ -1298,11 +1298,11 @@ test_synthetic_time_quanta() assert(false); // Check scheduler stats. verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/1, - /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/0, + /*switch_input_to_idle=*/1, /*switch_idle_to_input=*/0, /*switch_nop=*/1, /*preempts=*/2, /*direct_attempts=*/0, /*direct_successes=*/0); verify_scheduler_stats(scheduler.get_stream(1), /*switch_input_to_input=*/2, - /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/1, + /*switch_input_to_idle=*/1, /*switch_idle_to_input=*/1, /*switch_nop=*/0, /*preempts=*/1, /*direct_attempts=*/0, /*direct_successes=*/0); } @@ -1859,11 +1859,11 @@ test_synthetic_with_syscalls_multiple() // appearing in between (and ignoring the last letter for an input: EOF doesn't // count as a preempt). verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/17, - /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/1, + /*switch_input_to_idle=*/2, /*switch_idle_to_input=*/1, /*switch_nop=*/2, /*preempts=*/11, /*direct_attempts=*/0, /*direct_successes=*/0); verify_scheduler_stats(scheduler.get_stream(1), /*switch_input_to_input=*/16, - /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/1, + /*switch_input_to_idle=*/1, /*switch_idle_to_input=*/1, /*switch_nop=*/0, /*preempts=*/10, /*direct_attempts=*/0, /*direct_successes=*/0); } @@ -4259,7 +4259,7 @@ test_direct_switch() } assert(sched_as_string[0] == CORE0_SCHED_STRING); verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/3, - /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/1, + /*switch_input_to_idle=*/1, /*switch_idle_to_input=*/1, /*switch_nop=*/0, /*preempts=*/0, /*direct_attempts=*/3, /*direct_successes=*/2); } @@ -4300,7 +4300,7 @@ test_direct_switch() } assert(sched_as_string[0] == CORE0_SCHED_STRING); verify_scheduler_stats(scheduler.get_stream(0), /*switch_input_to_input=*/2, - /*switch_input_to_idle=*/0, /*switch_idle_to_input=*/2, + /*switch_input_to_idle=*/2, /*switch_idle_to_input=*/2, /*switch_nop=*/0, /*preempts=*/0, /*direct_attempts=*/0, /*direct_successes=*/0); }