RFC 016 Chunk 2c: approximate per-actor time-budget (reductions-like)
Accumulate on-CPU cycles per actor as ActorInfo.budget_cycles, behind the off-by-default budget-accounting feature (D6) — a reductions-style work metric for relative comparison across runs. - Approximate by design (per Mark): charge now - slice-start once at the yield point, reusing the timestamp reset_timeslice already sets, so one RDTSC per resume not two. Wake-slot resumes inherit the slice and so slightly over-attribute the chain's time to the woken actor — noise that averages out; we trade exactness for half the hot-path cost. - Field/ActorInfo member are unconditional (keeps the snapshot shape stable across the feature flag, D1); only the accumulation is gated, so default builds are byte-identical and pay nothing. Reads return 0 when off. Single-writer Relaxed like the other counters; reset in reset_counters (D7). Matrix: default + feature-on + rq-mpmc + rq-striped + release + trace all green; loom unaffected (feature off under --cfg loom).
This commit is contained in:
@@ -439,6 +439,12 @@ pub(crate) struct Slot {
|
||||
/// own thread, increments it on the receive path (D4/D5), Relaxed load+store
|
||||
/// with no RMW; snapshot reads Relaxed.
|
||||
messages_received: AtomicU64,
|
||||
/// RFC 016 Chunk 2 — cumulative on-CPU cycles this incarnation has consumed
|
||||
/// (the cycle-accurate "budget used", an analogue of OTP reductions).
|
||||
/// Written only by the scheduler thread that ran the actor, once per resume,
|
||||
/// and only when the `budget-accounting` feature is on (it costs two RDTSC
|
||||
/// per resume); stays 0 otherwise. Same single-writer Relaxed discipline.
|
||||
budget_cycles: AtomicU64,
|
||||
/// Cold lifecycle data. See [`SlotCold`].
|
||||
pub(crate) cold: RawMutex<SlotCold>,
|
||||
}
|
||||
@@ -452,6 +458,7 @@ impl Slot {
|
||||
closure: AtomicPtr::new(std::ptr::null_mut()),
|
||||
overruns: AtomicU64::new(0),
|
||||
messages_received: AtomicU64::new(0),
|
||||
budget_cycles: AtomicU64::new(0),
|
||||
cold: RawMutex::new(SlotCold {
|
||||
actor: None,
|
||||
waiters: Vec::new(),
|
||||
@@ -511,6 +518,26 @@ impl Slot {
|
||||
self.messages_received.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Accumulate on-CPU cycles consumed in one resume (RFC 016 Chunk 2,
|
||||
/// `budget-accounting`). Single-writer (the scheduler thread that ran the
|
||||
/// actor), Relaxed load+store. Approximate by design: the figure is
|
||||
/// `now − slice-start`, and a wake-slot resume inherits the waker's slice,
|
||||
/// so a handed-off actor is charged a little of the chain's time — noise
|
||||
/// that averages out across runs, traded for one RDTSC instead of two.
|
||||
#[cfg(feature = "budget-accounting")]
|
||||
#[inline]
|
||||
pub(crate) fn add_budget(&self, cycles: u64) {
|
||||
let v = self.budget_cycles.load(Ordering::Relaxed);
|
||||
self.budget_cycles.store(v.wrapping_add(cycles), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Read the accumulated budget cycles (Relaxed). Always 0 unless the
|
||||
/// `budget-accounting` feature is enabled.
|
||||
#[inline]
|
||||
pub(crate) fn budget_cycles(&self) -> u64 {
|
||||
self.budget_cycles.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Zero the per-actor introspection counters. Called at every point a slot
|
||||
/// is recycled or freshly occupied (`reclaim_slot`, `install_actor`) so a
|
||||
/// reused slot never carries a previous incarnation's counts — the standing
|
||||
@@ -519,6 +546,7 @@ impl Slot {
|
||||
pub(crate) fn reset_counters(&self) {
|
||||
self.overruns.store(0, Ordering::Relaxed);
|
||||
self.messages_received.store(0, Ordering::Relaxed);
|
||||
self.budget_cycles.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// A pid's-eye snapshot of the slot. Cold paths re-read this under the
|
||||
@@ -1530,6 +1558,11 @@ fn schedule_loop(inner: &Arc<RuntimeInner>, slot_idx: usize) {
|
||||
unsafe { switch_to_actor() };
|
||||
|
||||
PREEMPTION_ENABLED.with(|c| c.set(false));
|
||||
// RFC 016 Chunk 2: charge the cycles this resume consumed to the actor
|
||||
// (approximate; reuses the slice-start timestamp — one RDTSC). Read
|
||||
// before the next resume re-arms TIMESLICE_START.
|
||||
#[cfg(feature = "budget-accounting")]
|
||||
slot.add_budget(crate::preempt::elapsed_slice_cycles());
|
||||
stats.current_pid_index.store(u32::MAX, Ordering::Relaxed);
|
||||
clear_current_pid();
|
||||
crate::preempt::clear_current_stop();
|
||||
|
||||
Reference in New Issue
Block a user