Skip to content

Commit 2477025

Browse files
authored
Merge pull request #2039 from FractalFir/master
Add support for Performance / Efficency cores.
2 parents fa1f55a + 89efd0e commit 2477025

File tree

1 file changed

+72
-2
lines changed
  • collector/src/compile/execute

1 file changed

+72
-2
lines changed

collector/src/compile/execute/mod.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,67 @@ pub struct CargoProcess<'a> {
130130
pub touch_file: Option<String>,
131131
pub jobserver: Option<jobserver::Client>,
132132
}
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+
156+
#[cfg(not(target_os = "linux"))]
157+
// Modify this stub if you want to add support for P/E cores on more OSs
158+
fn performance_cores() -> Option<&'static String> {
159+
None
160+
}
161+
162+
#[cfg(target_os = "linux")]
163+
/// Makes the benchmark run only on Performance cores.
164+
fn run_on_p_cores(path: &Path, cpu_list: &str) -> Command {
165+
// Parse CPU list to extract the number of P cores!
166+
// This assumes the P core id's are countinus, in format `fisrt_id-last_id`
167+
let (core_start, core_end) = cpu_list
168+
.split_once("-")
169+
.unwrap_or_else(|| panic!("Unsuported P core list format: {cpu_list:?}."));
170+
let core_start: u32 = core_start
171+
.parse()
172+
.expect("Expected a number when parsing the start of the P core list!");
173+
let core_end: u32 = core_end
174+
.parse()
175+
.expect("Expected a number when parsing the end of the P core list!");
176+
let core_count = core_end - core_start;
177+
let mut cmd = Command::new("taskset");
178+
// Set job count to P core count - this is done for 2 reasons:
179+
// 1. The instruction count info for E core is often very incompleate - a substantial chunk of events is lost.
180+
// 2. The performance charcteristics of E cores are less reliable, so excluding them from the benchmark makes things easier.
181+
cmd.env("CARGO_BUILD_JOBS", format!("{core_count}"));
182+
// pass the P core list to taskset to pin task to the P core.
183+
cmd.arg("--cpu-list");
184+
cmd.arg(cpu_list);
185+
cmd.arg(path);
186+
cmd
187+
}
188+
189+
#[cfg(not(target_os = "linux"))]
190+
// Modify this stub if you want to add support for P/E cores on more OSs
191+
fn run_on_p_cores(_path: &Path, _cpu_list: &str) -> Command {
192+
todo!("Can't run commands on the P cores on this platform");
193+
}
133194

134195
impl<'a> CargoProcess<'a> {
135196
pub fn incremental(mut self, incremental: bool) -> Self {
@@ -149,7 +210,12 @@ impl<'a> CargoProcess<'a> {
149210
}
150211

151212
fn base_command(&self, cwd: &Path, subcommand: &str) -> Command {
152-
let mut cmd = Command::new(Path::new(&self.toolchain.components.cargo));
213+
// Processors with P and E cores require special handling
214+
let mut cmd = if let Some(p_cores) = performance_cores() {
215+
run_on_p_cores(Path::new(&self.toolchain.components.cargo), p_cores)
216+
} else {
217+
Command::new(Path::new(&self.toolchain.components.cargo))
218+
};
153219
cmd
154220
// Not all cargo invocations (e.g. `cargo clean`) need all of these
155221
// env vars set, but it doesn't hurt to have them.
@@ -550,7 +616,11 @@ fn process_stat_output(
550616
let mut parts = line.split(';').map(|s| s.trim());
551617
let cnt = get!(parts.next());
552618
let _unit = get!(parts.next());
553-
let name = get!(parts.next());
619+
let mut name = get!(parts.next());
620+
// Map P-core events to normal events
621+
if name == "cpu_core/instructions:u/" {
622+
name = "instructions:u";
623+
}
554624
let _time = get!(parts.next());
555625
let pct = get!(parts.next());
556626
if cnt == "<not supported>" || cnt == "<not counted>" || cnt.is_empty() {

0 commit comments

Comments
 (0)