Avoid fetch_add for context switch count.

Together with the previous commit, this reduces base IPC latency by 1%.
This commit is contained in:
4lDO2
2026-09-02 13:09:56 +02:00
parent 9329213e38
commit d2874a167d
4 changed files with 29 additions and 26 deletions
+5 -2
View File
@@ -15,6 +15,7 @@ use crate::{
switch::{BASE_SLICE_TICKS, NANOS_PER_TICK, SCALE, SCHED_PRIO_TO_WEIGHT, TICK_INTERVAL},
},
cpu_set::{LogicalCpuId, LogicalCpuSet},
cpu_stats::CpuStatsData,
ipi::{ipi, IpiKind, IpiTarget},
memory::{RmmA, RmmArch, TableKind},
percpu::PercpuBlock,
@@ -385,10 +386,11 @@ impl Drop for PreemptGuardL2<'_> {
}
}
pub fn get_contexts_stats(token: &mut CleanLockToken) -> (usize, usize, usize) {
pub fn get_contexts_stats(token: &mut CleanLockToken) -> (usize, usize, usize, usize) {
let alive = contexts(token.downgrade()).len();
let mut running = 0;
let mut switches = 0;
for i in 0..crate::cpu_count() {
if let Some(percpu) = unsafe {
@@ -397,10 +399,11 @@ pub fn get_contexts_stats(token: &mut CleanLockToken) -> (usize, usize, usize) {
.as_ref()
} {
running += percpu.switch_internals.run_queue.lock().queue.len();
switches += CpuStatsData::from(&percpu.stats).context_switches as usize;
}
}
let blocked = alive.saturating_sub(running);
(alive, running, blocked)
(alive, running, blocked, switches)
}
+1 -1
View File
@@ -180,7 +180,7 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
let switch_time = crate::time::monotonic(token);
let percpu = PercpuBlock::current();
cpu_stats::add_context_switch();
percpu.stats.add_context_switch();
//set PIT Interrupt counter to 0, giving each process same amount of PIT ticks
percpu.switch_internals.pit_ticks.set(0);
+19 -20
View File
@@ -4,9 +4,6 @@ use core::{
sync::atomic::{AtomicU64, AtomicU8, AtomicUsize, Ordering},
};
// Note: Using AtomicUsize rather than AtomicU64 as 32bit x86 doesn't support the latter
/// The number of times (overall) where a CPU switched from one context to another.
static CONTEXT_SWITCH_COUNT: AtomicUsize = AtomicUsize::new(0);
/// Number of times each Interrupt happened.
// TODO: isn't this already tracked by the irq scheme?
static IRQ_COUNT: [AtomicUsize; 256] = [const { AtomicUsize::new(0) }; 256];
@@ -42,6 +39,8 @@ pub struct CpuStats {
irq: AtomicU64,
/// Current state of the CPU
state: AtomicU8,
/// The number of times (overall) where a CPU switched from one context to another.
context_switches: AtomicU64,
}
impl CpuStats {
@@ -53,6 +52,7 @@ impl CpuStats {
idle: AtomicU64::new(0),
irq: AtomicU64::new(0),
state: AtomicU8::new(0),
context_switches: AtomicU64::new(0),
}
}
}
@@ -68,13 +68,12 @@ pub struct CpuStatsData {
pub idle: u64,
/// Number of times the CPU handled an interrupt
pub irq: u64,
/// Number of context switches on this CPU
pub context_switches: u64,
}
impl CpuStats {
/// Set the CPU's current state
///
/// # Parameters
/// * `new_state` - The state of the CPU for the following ticks.
/// Set the CPU's current state for the following ticks.
#[inline]
pub fn set_state(&self, new_state: CpuState) {
self.state.store(new_state as u8, Ordering::Relaxed);
@@ -116,14 +115,24 @@ impl CpuStats {
self.irq
.store(self.irq.load(Ordering::Relaxed) + 1, Ordering::Relaxed)
}
/// Add a context switch to the count.
#[inline]
pub fn add_context_switch(&self) {
// Again this is percpu, so fetch_add is unnecessary.
self.context_switches.store(
self.context_switches.load(Ordering::Relaxed) + 1,
Ordering::Relaxed,
);
}
}
impl fmt::Display for CpuStatsData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} {} {} {}",
self.user, self.nice, self.kernel, self.idle, self.irq,
"{} {} {} {} {} {}",
self.user, self.nice, self.kernel, self.idle, self.irq, self.context_switches
)
}
}
@@ -136,21 +145,11 @@ impl From<&CpuStats> for CpuStatsData {
kernel: val.kernel.load(Ordering::Relaxed),
idle: val.idle.load(Ordering::Relaxed),
irq: val.irq.load(Ordering::Relaxed),
context_switches: val.context_switches.load(Ordering::Relaxed),
}
}
}
/// Add a context switch to the count.
#[inline]
pub fn add_context_switch() {
CONTEXT_SWITCH_COUNT.fetch_add(1, Ordering::Relaxed);
}
/// Get the number of context switches.
pub fn get_context_switch_count() -> usize {
CONTEXT_SWITCH_COUNT.load(Ordering::Relaxed)
}
/// Add a context creation to the count.
#[inline]
pub fn add_context() {
+4 -3
View File
@@ -2,7 +2,7 @@ use core::fmt::Write as _;
use crate::{
context::get_contexts_stats,
cpu_stats::{get_context_switch_count, get_contexts_count, irq_counts},
cpu_stats::{get_contexts_count, irq_counts},
event::get_event_stat,
percpu::get_all_stats,
sync::CleanLockToken,
@@ -15,7 +15,8 @@ use alloc::{string::String, vec::Vec};
pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
let start_time_sec = *START.lock(token.token()) / 1_000_000_000;
let (contexts_alive, contexts_running, contexts_blocked) = get_contexts_stats(token);
let (contexts_alive, contexts_running, contexts_blocked, context_switches) =
get_contexts_stats(token);
let (event_keys, event_subs) = get_event_stat(token);
let (futex_keys, futex_subs) = get_futex_stat(token);
let pipe_subs = crate::scheme::pipe::get_pipe_stat(token);
@@ -36,7 +37,7 @@ pub fn resource(token: &mut CleanLockToken) -> Result<Vec<u8>> {
timeout_subscribers: {timeout_subs}\n",
get_cpu_stats(),
get_irq_stats(),
get_context_switch_count(),
context_switches,
get_contexts_count(),
);