Skip to content

Commit 7dd0821

Browse files
committed
Added support for Performance / Efficency cores.
1 parent fa1f55a commit 7dd0821

File tree

1 file changed

+69
-3
lines changed
  • collector/src/compile/execute

1 file changed

+69
-3
lines changed

collector/src/compile/execute/mod.rs

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,64 @@ pub struct CargoProcess<'a> {
130130
pub touch_file: Option<String>,
131131
pub jobserver: Option<jobserver::Client>,
132132
}
133-
133+
/// Returns an optional list of Performance CPU cores, if the system has P and E cores.
134+
/// This list *should* be in a format suitable for the `taskset` command.
135+
#[cfg(target_os = "linux")]
136+
fn performance_cores() -> Option<&'static String> {
137+
use std::sync::LazyLock;
138+
static PERFORMANCE_CORES: LazyLock<Option<String>> = LazyLock::new(|| {
139+
if std::fs::exists("/sys/devices/cpu").expect("Could not check the CPU architecture details: could not check if `/sys/devices/cpu` exists!"){
140+
// If /sys/devices/cpu exists, then this is not a "Performance-hybrid" CPU.
141+
None
142+
}
143+
else if std::fs::exists("/sys/devices/cpu_core").expect("Could not check the CPU architecture detali: could not check if `/sys/devices/cpu_core` exists!") {
144+
// If /sys/devices/cpu_core exists, then this is a "Performance-hybrid" CPU.
145+
eprintln!("WARNING: Performance-Hybrid CPU detected. `rustc-perf` can't run properly on Efficency cores: test suite will only use Performance cores!");
146+
Some(std::fs::read_to_string("/sys/devices/cpu_core/cpus").unwrap().trim().to_string())
147+
}else{
148+
// If neither dir exists, then something is wrong - `/sys/devices/cpu` has been in Linux for over a decade.
149+
eprintln!("WARNING: neither `/sys/devices/cpu` nor `/sys/devices/cpu_core` present, unable to determine if this CPU has a Performance-Hybrid architecture.");
150+
None
151+
}
152+
});
153+
(*PERFORMANCE_CORES).as_ref()
154+
}
155+
#[cfg(not(target_os = "linux"))]
156+
// Modify this stub if you want to add support for P/E cores on more OSs
157+
fn performance_cores() -> Option<&'static String> {
158+
None
159+
}
160+
#[cfg(target_os = "linux")]
161+
/// Makes the benchmark run only on Performance cores.
162+
fn run_on_p_cores(path: &Path, cpu_list: &str) -> Command {
163+
// Parse CPU list to extract the number of P cores!
164+
// This assumes the P core id's are countinus, in format `fisrt_id-last_id`
165+
let (core_start, core_end) = cpu_list
166+
.split_once("-")
167+
.unwrap_or_else(|| panic!("Unsuported P core list format: {cpu_list:?}."));
168+
let core_start: u32 = core_start
169+
.parse()
170+
.expect("Expected a number when parsing the start of the P core list!");
171+
let core_end: u32 = core_end
172+
.parse()
173+
.expect("Expected a number when parsing the end of the P core list!");
174+
let core_count = core_end - core_start;
175+
let mut cmd = Command::new("taskset");
176+
// Set job count to P core count - this is done for 2 reasons:
177+
// 1. The instruction count info for E core is often very incompleate - a substantial chunk of events is lost.
178+
// 2. The performance charcteristics of E cores are less reliable, so excluding them from the benchmark makes things easier.
179+
cmd.env("CARGO_BUILD_JOBS", format!("{core_count}"));
180+
// pass the P core list to taskset to pin task to the P core.
181+
cmd.arg("--cpu-list");
182+
cmd.arg(cpu_list);
183+
cmd.arg(path);
184+
cmd
185+
}
186+
#[cfg(not(target_os = "linux"))]
187+
// Modify this stub if you want to add support for P/E cores on more OSs
188+
fn run_on_p_cores(_path: &Path, _cpu_list: &str) -> Command {
189+
todo!("Can't run comamnds on the P cores on this platform")
190+
}
134191
impl<'a> CargoProcess<'a> {
135192
pub fn incremental(mut self, incremental: bool) -> Self {
136193
self.incremental = incremental;
@@ -149,7 +206,12 @@ impl<'a> CargoProcess<'a> {
149206
}
150207

151208
fn base_command(&self, cwd: &Path, subcommand: &str) -> Command {
152-
let mut cmd = Command::new(Path::new(&self.toolchain.components.cargo));
209+
// Processors with P and E cores require special handling
210+
let mut cmd = if let Some(p_cores) = performance_cores() {
211+
run_on_p_cores(Path::new(&self.toolchain.components.cargo), p_cores)
212+
} else {
213+
Command::new(Path::new(&self.toolchain.components.cargo))
214+
};
153215
cmd
154216
// Not all cargo invocations (e.g. `cargo clean`) need all of these
155217
// env vars set, but it doesn't hurt to have them.
@@ -550,7 +612,11 @@ fn process_stat_output(
550612
let mut parts = line.split(';').map(|s| s.trim());
551613
let cnt = get!(parts.next());
552614
let _unit = get!(parts.next());
553-
let name = get!(parts.next());
615+
let mut name = get!(parts.next());
616+
// Map P-core events to normal events
617+
if name == "cpu_core/instructions:u/" {
618+
name = "instructions:u";
619+
}
554620
let _time = get!(parts.next());
555621
let pct = get!(parts.next());
556622
if cnt == "<not supported>" || cnt == "<not counted>" || cnt.is_empty() {

0 commit comments

Comments
 (0)