From ae3e1cda100c4494fcc378e0cc8989864c894a67 Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Sat, 18 Jul 2026 23:51:48 -0700 Subject: [PATCH] feat(board): thread-contiguous logical CPU ID mapping Signed-off-by: Changyuan Lyu --- alioth-cli/src/boot/boot_test.rs | 5 ++-- alioth/src/board/board.rs | 30 ++++++++++++++----- .../src/board/board_amd64/board_amd64_test.rs | 22 ++++++++------ alioth/src/board/board_arm64_test.rs | 6 ++-- alioth/src/board/board_test.rs | 28 +++++++++++------ 5 files changed, 61 insertions(+), 30 deletions(-) diff --git a/alioth-cli/src/boot/boot_test.rs b/alioth-cli/src/boot/boot_test.rs index 1f950794..a29bb889 100644 --- a/alioth-cli/src/boot/boot_test.rs +++ b/alioth-cli/src/boot/boot_test.rs @@ -106,6 +106,7 @@ fn test_parse_args() { smt: true, cores: 8, sockets: 1, + thread_contiguous: false, }, }, mem: MemSpec { @@ -242,7 +243,7 @@ fn test_parse_net_arg(#[case] arg: &str, #[case] want: NetSpec) { HashMap::from([("id_topo", "cores=8,sockets=2,smt=false")]), CpuSpec { count: 16, - topology: CpuTopology { smt: false, cores: 8, sockets: 2 }, + topology: CpuTopology { smt: false, cores: 8, sockets: 2, thread_contiguous: false }, } )] #[case( @@ -251,7 +252,7 @@ fn test_parse_net_arg(#[case] arg: &str, #[case] want: NetSpec) { HashMap::new(), CpuSpec { count: 16, - topology: CpuTopology { smt: false, cores: 16, sockets: 1 } + topology: CpuTopology { smt: false, cores: 16, sockets: 1, thread_contiguous: false } } )] fn test_parse_cpu_arg( diff --git a/alioth/src/board/board.rs b/alioth/src/board/board.rs index be59d2dd..e86431b2 100644 --- a/alioth/src/board/board.rs +++ b/alioth/src/board/board.rs @@ -84,20 +84,36 @@ pub struct CpuTopology { #[serde(default)] /// Number of sockets. pub sockets: u8, + #[serde(default)] + /// Use thread-contiguous logical CPU ID mapping when SMT is on. + pub thread_contiguous: bool, } impl CpuTopology { pub fn encode(&self, index: u16) -> (u8, u16, u8) { - let total_cores = self.cores * self.sockets as u16; - let thread_id = index / total_cores; - let core_id = index % total_cores % self.cores; - let socket_id = index % total_cores / self.cores; - (socket_id as u8, core_id, thread_id as u8) + if self.thread_contiguous { + let threads_per_core = if self.smt { 2 } else { 1 }; + let thread_id = index % threads_per_core; + let core_id = index / threads_per_core % self.cores; + let socket_id = index / threads_per_core / self.cores; + (socket_id as u8, core_id, thread_id as u8) + } else { + let total_cores = self.cores * self.sockets as u16; + let thread_id = index / total_cores; + let core_id = index % total_cores % self.cores; + let socket_id = index % total_cores / self.cores; + (socket_id as u8, core_id, thread_id as u8) + } } pub fn decode(&self, socket_id: u8, core_id: u16, thread_id: u8) -> u16 { - let total_cores = self.cores * self.sockets as u16; - thread_id as u16 * total_cores + core_id + socket_id as u16 * self.cores + if self.thread_contiguous { + let threads_per_core = if self.smt { 2 } else { 1 }; + (socket_id as u16 * self.cores + core_id) * threads_per_core + thread_id as u16 + } else { + let total_cores = self.cores * self.sockets as u16; + thread_id as u16 * total_cores + core_id + socket_id as u16 * self.cores + } } } diff --git a/alioth/src/board/board_amd64/board_amd64_test.rs b/alioth/src/board/board_amd64/board_amd64_test.rs index 1f5af7d9..419880fc 100644 --- a/alioth/src/board/board_amd64/board_amd64_test.rs +++ b/alioth/src/board/board_amd64/board_amd64_test.rs @@ -18,15 +18,19 @@ use crate::board::CpuTopology; use crate::board::x86_64::encode_x2apic_id; #[rstest] -#[case(CpuTopology{smt: false, cores: 1, sockets: 1}, 0, 0)] -#[case(CpuTopology{smt: true, cores: 2, sockets: 1}, 0, 0)] -#[case(CpuTopology{smt: true, cores: 2, sockets: 1}, 1, 2)] -#[case(CpuTopology{smt: true, cores: 2, sockets: 1}, 2, 1)] -#[case(CpuTopology{smt: true, cores: 2, sockets: 1}, 3, 3)] -#[case(CpuTopology{smt: true, cores: 6, sockets: 2}, 4, 8)] -#[case(CpuTopology{smt: true, cores: 6, sockets: 2}, 11, 26)] -#[case(CpuTopology{smt: true, cores: 6, sockets: 2}, 14, 5)] -#[case(CpuTopology{smt: true, cores: 6, sockets: 2}, 23, 27)] +#[case(CpuTopology{smt: false, cores: 1, sockets: 1, ..Default::default()}, 0, 0)] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: false}, 0, 0)] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: false}, 1, 2)] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: false}, 2, 1)] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: false}, 3, 3)] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: true}, 0, 0)] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: true}, 1, 1)] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: true}, 2, 2)] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: true}, 3, 3)] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: false}, 4, 8)] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: false}, 11, 26)] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: false}, 14, 5)] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: false}, 23, 27)] fn test_encode_x2apic(#[case] topology: CpuTopology, #[case] index: u16, #[case] x2apic: u32) { assert_eq!(encode_x2apic_id(&topology, index), x2apic) } diff --git a/alioth/src/board/board_arm64_test.rs b/alioth/src/board/board_arm64_test.rs index 4e61a89e..070fb8d8 100644 --- a/alioth/src/board/board_arm64_test.rs +++ b/alioth/src/board/board_arm64_test.rs @@ -19,9 +19,9 @@ use crate::board::CpuTopology; use crate::board::aarch64::encode_mpidr; #[rstest] -#[case(CpuTopology{smt: false, cores: 1, sockets: 1}, 1, 1)] -#[case(CpuTopology{smt: true, cores: 8, sockets: 1}, 8, 1)] -#[case(CpuTopology{smt: true, cores: 8, sockets: 4}, 45, (1 << 16) | (5 << 8) | 1)] +#[case(CpuTopology{smt: false, cores: 1, sockets: 1, ..Default::default()}, 1, 1)] +#[case(CpuTopology{smt: true, cores: 8, sockets: 1, thread_contiguous: false}, 8, 1)] +#[case(CpuTopology{smt: true, cores: 8, sockets: 4, thread_contiguous: false}, 45, (1 << 16) | (5 << 8) | 1)] fn test_encode_mpidr(#[case] topology: CpuTopology, #[case] index: u16, #[case] mpidr: u64) { assert_eq!(encode_mpidr(&topology, index), MpidrEl1(mpidr)); } diff --git a/alioth/src/board/board_test.rs b/alioth/src/board/board_test.rs index e2f3d223..155c6069 100644 --- a/alioth/src/board/board_test.rs +++ b/alioth/src/board/board_test.rs @@ -23,6 +23,7 @@ use crate::board::{CpuSpec, CpuTopology}; smt: false, cores: 2, sockets: 1, + ..Default::default() }, }, true)] #[case(CpuSpec { @@ -31,6 +32,7 @@ use crate::board::{CpuSpec, CpuTopology}; smt: true, cores: 2, sockets: 1, + ..Default::default() }, }, false)] fn test_cpu_topology_validate(#[case] spec: CpuSpec, #[case] expected: bool) { @@ -38,15 +40,23 @@ fn test_cpu_topology_validate(#[case] spec: CpuSpec, #[case] expected: bool) { } #[rstest] -#[case(CpuTopology{smt: false, cores: 1, sockets: 1}, 0, (0, 0, 0))] -#[case(CpuTopology{smt: true, cores: 2, sockets: 1}, 0, (0, 0, 0))] -#[case(CpuTopology{smt: true, cores: 2, sockets: 1}, 1, (0, 1, 0))] -#[case(CpuTopology{smt: true, cores: 2, sockets: 1}, 2, (0, 0, 1))] -#[case(CpuTopology{smt: true, cores: 2, sockets: 1}, 3, (0, 1, 1))] -#[case(CpuTopology{smt: true, cores: 6, sockets: 2}, 4, (0, 4, 0))] -#[case(CpuTopology{smt: true, cores: 6, sockets: 2}, 11, (1, 5, 0))] -#[case(CpuTopology{smt: true, cores: 6, sockets: 2}, 14, (0, 2, 1))] -#[case(CpuTopology{smt: true, cores: 6, sockets: 2}, 23, (1, 5, 1))] +#[case(CpuTopology{smt: false, cores: 1, sockets: 1, thread_contiguous: false}, 0, (0, 0, 0))] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: false}, 0, (0, 0, 0))] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: false}, 1, (0, 1, 0))] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: false}, 2, (0, 0, 1))] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: false}, 3, (0, 1, 1))] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: false}, 4, (0, 4, 0))] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: false}, 11, (1, 5, 0))] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: false}, 14, (0, 2, 1))] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: false}, 23, (1, 5, 1))] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: true}, 0, (0, 0, 0))] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: true}, 1, (0, 0, 1))] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: true}, 2, (0, 1, 0))] +#[case(CpuTopology{smt: true, cores: 2, sockets: 1, thread_contiguous: true}, 3, (0, 1, 1))] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: true}, 4, (0, 2, 0))] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: true}, 11, (0, 5, 1))] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: true}, 14, (1, 1, 0))] +#[case(CpuTopology{smt: true, cores: 6, sockets: 2, thread_contiguous: true}, 23, (1, 5, 1))] fn test_cpu_topology_encode_decode( #[case] topology: CpuTopology, #[case] index: u16,