Skip to content

Commit 898d3fb

Browse files
authored
i#7157 dyn inject: Fix extra switch injections (#7302)
Fixes a bug in the dynamic injection logic in the drmemtrace scheduler that caused the context switch sequence to be injected more times than actual context switches. It was erroneously injected also when set_cur_input() was invoked without any change to the input. update_switch_stats() is a better control point for switch sequence injection, which is renamed to on_context_switch() here. Now, in the switch_insertion offline test, we have 11 context switch injections (== input-to-input + idle-to-input switches), instead of the prior 363. We deliberately do not inject the context switch sequence on input-to-idle transitions. Note that we already inject on idle-to-input transitions, and it is not clear yet whether we want to repeat it at input-to-idle also or maybe inject some different sequence. The above move also removes the use of get_instruction_ordinal() to determine whether switch records should be inserted. Existing logic checked for get_instruction_ordinal() > 0 to avoid inserting switch records before the very first input on an output. This does not work as required when USE_INPUT_ORDINALS is set, as then the input-local instruction ordinal instead (which is non-zero as it is not adjusted for the read-ahead instructions) is returned, which ends up inserting the switch sequence before the very first input also. A scheduler unit_test that uses USE_INPUT_ORDINALS is coming up in #7299 with more fixes. Issue: #7157
1 parent a0abe3c commit 898d3fb

4 files changed

Lines changed: 66 additions & 49 deletions

File tree

clients/drcachesim/common/memtrace_stream.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class memtrace_stream_t {
113113
*/
114114
SCHED_STAT_HIT_OUTPUT_LIMIT,
115115
/**
116-
* Counts the instances when the context switch sequence was injected.
116+
* Counts the instances when the kernel context switch sequence was injected.
117117
*/
118118
SCHED_STAT_KERNEL_SWITCH_SEQUENCE_INJECTIONS,
119119
/** Count of statistic types. */

clients/drcachesim/scheduler/scheduler_impl.cpp

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* **********************************************************
2-
* Copyright (c) 2023-2024 Google, Inc. All rights reserved.
2+
* Copyright (c) 2023-2025 Google, Inc. All rights reserved.
33
* **********************************************************/
44

55
/*
@@ -2288,41 +2288,6 @@ scheduler_impl_tmpl_t<RecordType, ReaderType>::set_cur_input(
22882288
stream->page_size_ = inputs_[input].reader->get_page_size();
22892289
}
22902290

2291-
if (inputs_[input].pid != INVALID_PID) {
2292-
insert_switch_tid_pid(inputs_[input]);
2293-
}
2294-
2295-
if (!switch_sequence_.empty() &&
2296-
outputs_[output].stream->get_instruction_ordinal() > 0) {
2297-
switch_type_t switch_type = sched_type_t::SWITCH_INVALID;
2298-
if (prev_workload != inputs_[input].workload)
2299-
switch_type = sched_type_t::SWITCH_PROCESS;
2300-
else
2301-
switch_type = sched_type_t::SWITCH_THREAD;
2302-
// Inject kernel context switch code. Since the injected records belong to this
2303-
// input (the kernel is acting on behalf of this input) we insert them into the
2304-
// input's queue, but ahead of any prior queued items. This is why we walk in
2305-
// reverse, for the push_front calls to the deque. We update the tid of the
2306-
// records here to match. They are considered as is_record_synthetic() and do
2307-
// not affect input stream ordinals.
2308-
// XXX: These will appear before the top headers of a new thread which is slightly
2309-
// odd to have regular records with the new tid before the top headers.
2310-
if (!switch_sequence_[switch_type].empty()) {
2311-
++outputs_[output]
2312-
.stats[memtrace_stream_t::SCHED_STAT_KERNEL_SWITCH_SEQUENCE_INJECTIONS];
2313-
for (int i = static_cast<int>(switch_sequence_[switch_type].size()) - 1;
2314-
i >= 0; --i) {
2315-
RecordType record = switch_sequence_[switch_type][i];
2316-
record_type_set_tid(record, inputs_[input].tid);
2317-
inputs_[input].queue.push_front(record);
2318-
}
2319-
VPRINT(this, 3,
2320-
"Inserted %zu switch records for type %d from %d.%d to %d.%d\n",
2321-
switch_sequence_[switch_type].size(), switch_type, prev_workload,
2322-
outputs_[output].prev_input, inputs_[input].workload, input);
2323-
}
2324-
}
2325-
23262291
inputs_[input].prev_time_in_quantum =
23272292
outputs_[output].cur_time->load(std::memory_order_acquire);
23282293

@@ -2415,28 +2380,80 @@ scheduler_impl_tmpl_t<RecordType, ReaderType>::pick_next_input(output_ordinal_t
24152380
}
24162381
// We can't easily place these stats inside set_cur_input() as we call that to
24172382
// temporarily give up our input.
2418-
update_switch_stats(output, prev_index, index);
2383+
on_context_switch(output, prev_index, index);
24192384
set_cur_input(output, index);
24202385
return res;
24212386
}
24222387

24232388
template <typename RecordType, typename ReaderType>
24242389
void
2425-
scheduler_impl_tmpl_t<RecordType, ReaderType>::update_switch_stats(
2390+
scheduler_impl_tmpl_t<RecordType, ReaderType>::on_context_switch(
24262391
output_ordinal_t output, input_ordinal_t prev_input, input_ordinal_t new_input)
24272392
{
2393+
bool do_inject_switch_seq = false;
24282394
if (prev_input == new_input)
24292395
++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_NOP];
24302396
else if (prev_input != sched_type_t::INVALID_INPUT_ORDINAL &&
2431-
new_input != sched_type_t::INVALID_INPUT_ORDINAL)
2397+
new_input != sched_type_t::INVALID_INPUT_ORDINAL) {
24322398
++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_INPUT];
2433-
else if (new_input == sched_type_t::INVALID_INPUT_ORDINAL)
2399+
do_inject_switch_seq = true;
2400+
} else if (new_input == sched_type_t::INVALID_INPUT_ORDINAL) {
2401+
// XXX: For now, we do not inject a kernel context switch sequence on
2402+
// input-to-idle transitions (note that we do so on idle-to-input though).
2403+
// However, we may want to inject some other suitable sequence, but we're not
2404+
// sure yet.
24342405
++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_INPUT_TO_IDLE];
2435-
else {
2406+
} else {
24362407
++outputs_[output].stats[memtrace_stream_t::SCHED_STAT_SWITCH_IDLE_TO_INPUT];
24372408
// Reset the flag so we'll try to steal if we go idle again.
24382409
outputs_[output].tried_to_steal_on_idle = false;
2410+
do_inject_switch_seq = true;
24392411
}
2412+
2413+
// We want to insert the context switch sequence on input-to-input and
2414+
// idle-to-input cases. This is a better control point to do that than
2415+
// set_cur_input. Here we get the stolen input events too, and we don't have
2416+
// to filter out the init-time set_cur_input cases.
2417+
if (!do_inject_switch_seq)
2418+
return;
2419+
if (inputs_[new_input].pid != INVALID_PID) {
2420+
insert_switch_tid_pid(inputs_[new_input]);
2421+
}
2422+
if (switch_sequence_.empty())
2423+
return;
2424+
switch_type_t switch_type = sched_type_t::SWITCH_INVALID;
2425+
if ( // XXX: idle-to-input transitions are assumed to be process switches
2426+
// for now. But we may want to improve this heuristic.
2427+
prev_input == sched_type_t::INVALID_INPUT_ORDINAL ||
2428+
inputs_[prev_input].workload != inputs_[new_input].workload)
2429+
switch_type = sched_type_t::SWITCH_PROCESS;
2430+
else
2431+
switch_type = sched_type_t::SWITCH_THREAD;
2432+
if (switch_sequence_[switch_type].empty())
2433+
return;
2434+
// Inject kernel context switch code. Since the injected records belong to
2435+
// this input (the kernel is acting on behalf of this input) we insert them
2436+
// into the input's queue, but ahead of any prior queued items. This is why
2437+
// we walk in reverse, for the push_front calls to the deque. We update the
2438+
// tid of the records here to match. They are considered as
2439+
// is_record_synthetic() and do not affect input stream ordinals.
2440+
// XXX: These will appear before the top headers of a new thread which is
2441+
// slightly odd to have regular records with the new tid before the top
2442+
// headers.
2443+
++outputs_[output]
2444+
.stats[memtrace_stream_t::SCHED_STAT_KERNEL_SWITCH_SEQUENCE_INJECTIONS];
2445+
for (int i = static_cast<int>(switch_sequence_[switch_type].size()) - 1; i >= 0;
2446+
--i) {
2447+
RecordType record = switch_sequence_[switch_type][i];
2448+
record_type_set_tid(record, inputs_[new_input].tid);
2449+
inputs_[new_input].queue.emplace_front(record);
2450+
}
2451+
VPRINT(this, 3, "Inserted %zu switch for type %d from %d.%d to %d.%d\n",
2452+
switch_sequence_[switch_type].size(), switch_type,
2453+
prev_input != sched_type_t::INVALID_INPUT_ORDINAL
2454+
? inputs_[prev_input].workload
2455+
: -1,
2456+
prev_input, inputs_[new_input].workload, new_input);
24402457
}
24412458

24422459
template <typename RecordType, typename ReaderType>
@@ -2655,7 +2672,7 @@ scheduler_impl_tmpl_t<RecordType, ReaderType>::next_record(output_ordinal_t outp
26552672
} else if (res == sched_type_t::STATUS_STOLE) {
26562673
// We need to loop to get the new record.
26572674
input = &inputs_[outputs_[output].cur_input];
2658-
update_switch_stats(output, prev_input, input->index);
2675+
on_context_switch(output, prev_input, input->index);
26592676
lock.unlock();
26602677
lock = std::unique_lock<mutex_dbg_owned>(*input->lock);
26612678
lock.lock();

clients/drcachesim/scheduler/scheduler_impl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,8 @@ template <typename RecordType, typename ReaderType> class scheduler_impl_tmpl_t
406406
scale_blocked_time(uint64_t initial_time) const;
407407

408408
void
409-
update_switch_stats(output_ordinal_t output, input_ordinal_t prev_input,
410-
input_ordinal_t new_input);
409+
on_context_switch(output_ordinal_t output, input_ordinal_t prev_input,
410+
input_ordinal_t new_input);
411411

412412
///
413413
///////////////////////////////////////////////////////////////////////////

clients/drcachesim/tests/switch_insertion.templatex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@ Schedule stats tool results:
22
Total counts:
33
4 cores
44
.*
5-
639664 instructions
5+
638960 instructions
66
11 total context switches
77
.*
88
6 voluntary context switches
99
0 direct context switches
10-
363 context switch sequence injections
10+
11 context switch sequence injections
1111
.*
1212
9 switches input-to-input
1313
5 switches input-to-idle
1414
2 switches idle-to-input
1515
.*
1616
Core #0 counts:
1717
.*
18-
117223 instructions
18+
117031 instructions
1919
8 total context switches
2020
.*
2121
3 voluntary context switches
2222
0 direct context switches
23-
104 context switch sequence injections
23+
8 context switch sequence injections
2424
.*
2525
6 switches input-to-input
2626
3 switches input-to-idle

0 commit comments

Comments
 (0)