use dashmap::DashMap;
use std::{
panic::Location,
sync::Arc,
time::{Duration, Instant},
};
const PREFERRED_SAMPLE_INTERVAL: u32 = 10;
const RESAMPLE_INTERVAL: u32 = 100;
const MAX_RESAMPLE_SHIFT: u32 = 5;
const SERIAL_SAMPLE_BUDGET_NS: u64 = 10_000_000;
const SPAWN_INLINE_BUDGET_NS: u64 = 1_000_000;
const EWMA_PREVIOUS_WEIGHT: u64 = 4;
const EWMA_NEXT_WEIGHT: u64 = 1;
const EWMA_WEIGHT: u64 = EWMA_PREVIOUS_WEIGHT + EWMA_NEXT_WEIGHT;
type RunEntries = DashMap<Key, RunEntry>;
type SpawnEntries = DashMap<Key, SpawnEntry>;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum RunExecution {
Serial,
Parallel,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum SpawnExecution {
Inline,
Offload,
}
#[derive(Clone, Debug, Default)]
pub(super) struct Policy {
run_entries: Arc<RunEntries>,
spawn_entries: Arc<SpawnEntries>,
}
impl Policy {
pub(super) fn try_run<R, E>(
&self,
caller: &'static Location<'static>,
len: usize,
work: usize,
parallelism: usize,
run: impl FnOnce(RunExecution) -> Result<R, E>,
) -> Result<R, E> {
if parallelism <= 1 {
return run(RunExecution::Serial);
}
let key = Key::new(caller, len, work, parallelism);
let (execution, measure) = self.run_entries.entry(key).or_default().choose(parallelism);
let start = measure.then(Instant::now);
let result = run(execution);
if let (Some(start), Ok(_)) = (start, &result) {
let mut entry = self.run_entries.entry(key).or_default();
entry.record(execution, start.elapsed());
}
result
}
pub(super) fn choose_spawn(
&self,
caller: &'static Location<'static>,
len: usize,
parallelism: usize,
) -> (SpawnExecution, bool) {
if parallelism <= 1 {
return (SpawnExecution::Inline, false);
}
let key = Key::new(caller, len, len, parallelism);
self.spawn_entries
.entry(key)
.or_default()
.choose(SPAWN_INLINE_BUDGET_NS)
}
pub(super) fn record_spawn_inline(
&self,
caller: &'static Location<'static>,
len: usize,
parallelism: usize,
job: Duration,
) {
self.record_spawn(caller, len, parallelism, job, |entry| &mut entry.inline_ns);
}
pub(super) fn record_spawn_job(
&self,
caller: &'static Location<'static>,
len: usize,
parallelism: usize,
job: Duration,
) {
self.record_spawn(caller, len, parallelism, job, |entry| &mut entry.job_ns);
}
pub(super) fn record_spawn_overhead(
&self,
caller: &'static Location<'static>,
len: usize,
parallelism: usize,
overhead: Duration,
) {
self.record_spawn(caller, len, parallelism, overhead, |entry| {
&mut entry.overhead_ns
});
}
fn record_spawn(
&self,
caller: &'static Location<'static>,
len: usize,
parallelism: usize,
sample: Duration,
estimate: impl FnOnce(&mut SpawnEntry) -> &mut Estimate,
) {
if parallelism <= 1 {
return;
}
let key = Key::new(caller, len, len, parallelism);
let mut entry = self.spawn_entries.entry(key).or_default();
estimate(&mut entry).record(u64::try_from(sample.as_nanos()).unwrap_or(u64::MAX));
}
#[cfg(test)]
pub(super) fn len(&self) -> usize {
self.run_entries.len()
}
#[cfg(test)]
pub(super) fn spawn_len(&self) -> usize {
self.spawn_entries.len()
}
#[cfg(test)]
pub(super) fn spawn_recorded(
&self,
caller: &'static Location<'static>,
len: usize,
parallelism: usize,
) -> bool {
let key = Key::new(caller, len, len, parallelism);
self.spawn_entries
.get(&key)
.is_some_and(|entry| entry.overhead_ns.get().is_some() || entry.job_ns.get().is_some())
}
#[cfg(test)]
pub(super) fn get_entry(
&self,
caller: &'static Location<'static>,
len: usize,
work: usize,
parallelism: usize,
) -> Option<(Option<u64>, Option<u64>)> {
let key = Key::new(caller, len, work, parallelism);
self.run_entries
.get(&key)
.map(|e| (e.serial_ns.get(), e.parallel_ns.get()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
struct Key {
file: &'static str,
line: u32,
column: u32,
len_bucket: u8,
work_bucket: u8,
parallelism: usize,
}
impl Key {
const fn new(
caller: &'static Location<'static>,
len: usize,
work: usize,
parallelism: usize,
) -> Self {
Self {
file: caller.file(),
line: caller.line(),
column: caller.column(),
len_bucket: len_bucket(len),
work_bucket: len_bucket(work),
parallelism,
}
}
}
#[derive(Clone, Copy, Debug, Default)]
struct Estimate(Option<u64>);
impl Estimate {
const fn get(&self) -> Option<u64> {
self.0
}
fn record(&mut self, sample_ns: u64) {
self.0 = Some(
self.0
.map_or(sample_ns, |current| update_ewma(current, sample_ns)),
);
}
}
#[derive(Clone, Copy, Debug, Default)]
struct Cadence {
since_probe: u32,
}
impl Cadence {
const fn saturate(&mut self) {
self.since_probe = u32::MAX;
}
fn arbitrate<P: Copy>(
&mut self,
preferred: P,
loser: P,
winner_ns: u64,
loser_ns: u64,
probe_allowed: bool,
) -> (P, bool) {
let slowdown = loser_ns / winner_ns.max(1);
let shift = slowdown
.saturating_sub(1)
.min(u64::from(MAX_RESAMPLE_SHIFT)) as u32;
let interval = RESAMPLE_INTERVAL << shift;
self.since_probe = self.since_probe.saturating_add(1);
if self.since_probe >= interval {
self.since_probe = 0;
if probe_allowed {
return (loser, true);
}
return (preferred, true);
}
(
preferred,
self.since_probe.is_multiple_of(PREFERRED_SAMPLE_INTERVAL),
)
}
}
#[derive(Clone, Copy, Debug, Default)]
struct RunEntry {
serial_ns: Estimate,
parallel_ns: Estimate,
cadence: Cadence,
}
impl RunEntry {
fn projected_serial(parallel_ns: u64, parallelism: usize) -> u64 {
parallel_ns.saturating_mul(u64::try_from(parallelism).unwrap_or(u64::MAX))
}
fn preferred(serial_ns: u64, parallel_ns: u64, parallelism: usize) -> RunExecution {
if Self::projected_serial(parallel_ns, parallelism) >= SERIAL_SAMPLE_BUDGET_NS
|| serial_ns >= SERIAL_SAMPLE_BUDGET_NS
|| parallel_ns < serial_ns
{
RunExecution::Parallel
} else {
RunExecution::Serial
}
}
fn choose(&mut self, parallelism: usize) -> (RunExecution, bool) {
let Some(parallel_ns) = self.parallel_ns.get() else {
self.cadence.saturate();
return (RunExecution::Parallel, true);
};
let can_sample_serial =
Self::projected_serial(parallel_ns, parallelism) < SERIAL_SAMPLE_BUDGET_NS;
let (preferred, loser, winner_ns, loser_ns) = self.serial_ns.get().map_or(
(
RunExecution::Parallel,
RunExecution::Serial,
parallel_ns,
parallel_ns,
),
|serial_ns| match Self::preferred(serial_ns, parallel_ns, parallelism) {
RunExecution::Serial => (
RunExecution::Serial,
RunExecution::Parallel,
serial_ns,
parallel_ns,
),
RunExecution::Parallel => (
RunExecution::Parallel,
RunExecution::Serial,
parallel_ns,
serial_ns,
),
},
);
let probe_allowed = preferred == RunExecution::Serial || can_sample_serial;
self.cadence
.arbitrate(preferred, loser, winner_ns, loser_ns, probe_allowed)
}
fn record(&mut self, execution: RunExecution, elapsed: Duration) {
let elapsed_ns = u64::try_from(elapsed.as_nanos()).unwrap_or(u64::MAX);
match execution {
RunExecution::Serial => self.serial_ns.record(elapsed_ns),
RunExecution::Parallel => self.parallel_ns.record(elapsed_ns),
}
}
}
#[derive(Clone, Copy, Debug, Default)]
struct SpawnEntry {
overhead_ns: Estimate,
job_ns: Estimate,
inline_ns: Estimate,
cadence: Cadence,
}
impl SpawnEntry {
fn choose(&mut self, budget: u64) -> (SpawnExecution, bool) {
let (Some(overhead_ns), Some(job_ns)) = (self.overhead_ns.get(), self.job_ns.get()) else {
self.cadence.saturate();
return (SpawnExecution::Offload, true);
};
let bound = self.inline_ns.get().unwrap_or(job_ns);
let threshold = overhead_ns.min(budget);
let (preferred, loser, winner_ns, loser_ns, probe_allowed) = if bound > threshold {
(
SpawnExecution::Offload,
SpawnExecution::Inline,
threshold,
bound,
job_ns < budget,
)
} else {
(
SpawnExecution::Inline,
SpawnExecution::Offload,
bound,
threshold,
true,
)
};
self.cadence
.arbitrate(preferred, loser, winner_ns, loser_ns, probe_allowed)
}
}
fn update_ewma(current: u64, next: u64) -> u64 {
let weighted = u128::from(current) * u128::from(EWMA_PREVIOUS_WEIGHT)
+ u128::from(next) * u128::from(EWMA_NEXT_WEIGHT);
(weighted / u128::from(EWMA_WEIGHT))
.try_into()
.unwrap_or(u64::MAX)
}
const fn len_bucket(len: usize) -> u8 {
if len == 0 {
0
} else {
(usize::BITS - len.leading_zeros()) as u8
}
}
#[cfg(test)]
mod tests {
use super::{
MAX_RESAMPLE_SHIFT, PREFERRED_SAMPLE_INTERVAL, Policy, RESAMPLE_INTERVAL, RunEntry,
RunExecution, SPAWN_INLINE_BUDGET_NS, SpawnEntry, SpawnExecution,
};
use std::{panic::Location, time::Duration};
const PARALLELISM: usize = 4;
fn choose(entry: &mut RunEntry) -> (RunExecution, bool) {
entry.choose(PARALLELISM)
}
#[test]
fn starts_parallel_then_seeds_serial_immediately() {
let mut entry = RunEntry::default();
assert_eq!(choose(&mut entry), (RunExecution::Parallel, true));
entry.record(RunExecution::Parallel, Duration::from_micros(100));
assert_eq!(choose(&mut entry), (RunExecution::Serial, true));
entry.record(RunExecution::Serial, Duration::from_micros(95));
for i in 1..RESAMPLE_INTERVAL {
assert_eq!(
choose(&mut entry),
(RunExecution::Serial, i % PREFERRED_SAMPLE_INTERVAL == 0)
);
}
assert_eq!(choose(&mut entry), (RunExecution::Parallel, true));
}
#[test]
fn defers_serial_seed_when_projection_exceeds_budget() {
let mut entry = RunEntry::default();
assert_eq!(choose(&mut entry), (RunExecution::Parallel, true));
entry.record(RunExecution::Parallel, Duration::from_millis(10));
assert_eq!(choose(&mut entry), (RunExecution::Parallel, true));
assert!(entry.serial_ns.get().is_none());
for i in 1..RESAMPLE_INTERVAL {
assert_eq!(
choose(&mut entry),
(RunExecution::Parallel, i % PREFERRED_SAMPLE_INTERVAL == 0)
);
}
}
#[test]
fn never_seeds_serial_when_projection_exceeds_budget() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_millis(10));
for i in 1..=(2 * RESAMPLE_INTERVAL) {
assert_eq!(
choose(&mut entry),
(RunExecution::Parallel, i % PREFERRED_SAMPLE_INTERVAL == 0)
);
}
assert!(entry.serial_ns.get().is_none());
}
#[test]
fn never_runs_serial_on_big_work() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_millis(25));
for _ in 0..10_000 {
let (execution, measure) = entry.choose(12);
assert_eq!(execution, RunExecution::Parallel);
if measure {
entry.record(RunExecution::Parallel, Duration::from_millis(25));
}
}
assert!(entry.serial_ns.get().is_none());
}
#[test]
fn big_serial_estimate_biases_parallel() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_millis(30));
entry.record(RunExecution::Serial, Duration::from_millis(12));
for _ in 0..(2 * RESAMPLE_INTERVAL) {
let (execution, _) = choose(&mut entry);
assert_eq!(execution, RunExecution::Parallel);
}
}
#[test]
fn projection_gates_preferred_serial() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_millis(25));
entry.record(RunExecution::Serial, Duration::from_millis(8));
for _ in 0..(2 * RESAMPLE_INTERVAL) {
let (execution, _) = entry.choose(12);
assert_eq!(execution, RunExecution::Parallel);
}
}
#[test]
fn prefers_serial_when_faster() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(100));
entry.record(RunExecution::Serial, Duration::from_micros(95));
assert_eq!(choose(&mut entry), (RunExecution::Serial, false));
}
#[test]
fn prefers_parallel_when_it_wins_wall_time() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(100));
entry.record(RunExecution::Serial, Duration::from_micros(200));
assert_eq!(choose(&mut entry), (RunExecution::Parallel, false));
}
#[test]
fn prefers_serial_on_tie() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(100));
entry.record(RunExecution::Serial, Duration::from_micros(100));
assert_eq!(choose(&mut entry), (RunExecution::Serial, false));
}
#[test]
fn pre_seed_parallel_samples_blend() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_millis(10));
entry.record(RunExecution::Parallel, Duration::from_millis(20));
assert_eq!(entry.parallel_ns.get(), Some(12_000_000));
}
#[test]
fn blends_preferred_samples_with_integer_math() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_nanos(1000));
entry.record(RunExecution::Serial, Duration::from_nanos(100));
entry.record(RunExecution::Serial, Duration::from_nanos(200));
assert_eq!(entry.serial_ns.get(), Some(120));
}
#[test]
fn blends_preferred_parallel_samples() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_nanos(100));
entry.record(RunExecution::Serial, Duration::from_nanos(1000));
entry.record(RunExecution::Parallel, Duration::from_nanos(200));
assert_eq!(entry.parallel_ns.get(), Some(120));
}
#[test]
fn probes_blend_into_stale_estimates() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_millis(25));
entry.record(RunExecution::Serial, Duration::from_millis(100));
entry.record(RunExecution::Serial, Duration::from_millis(5));
assert_eq!(entry.serial_ns.get(), Some(81_000_000));
assert_eq!(
RunEntry::preferred(
entry.serial_ns.get().unwrap(),
entry.parallel_ns.get().unwrap(),
PARALLELISM
),
RunExecution::Parallel
);
}
#[test]
fn seeds_serial_once_projection_shrinks_into_budget() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_millis(10));
for _ in 1..RESAMPLE_INTERVAL {
let (execution, measure) = choose(&mut entry);
assert_eq!(execution, RunExecution::Parallel);
if measure {
entry.record(RunExecution::Parallel, Duration::from_micros(100));
}
}
assert_eq!(choose(&mut entry), (RunExecution::Serial, true));
}
#[test]
fn resamples_other_execution() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(100));
entry.record(RunExecution::Serial, Duration::from_micros(80));
for i in 1..RESAMPLE_INTERVAL {
assert_eq!(
choose(&mut entry),
(RunExecution::Serial, i % PREFERRED_SAMPLE_INTERVAL == 0)
);
}
assert_eq!(choose(&mut entry), (RunExecution::Parallel, true));
}
#[test]
fn resamples_serial_when_parallel_wins() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(100));
entry.record(RunExecution::Serial, Duration::from_micros(150));
for i in 1..RESAMPLE_INTERVAL {
assert_eq!(
choose(&mut entry),
(RunExecution::Parallel, i % PREFERRED_SAMPLE_INTERVAL == 0)
);
}
assert_eq!(choose(&mut entry), (RunExecution::Serial, true));
}
#[test]
fn resample_interval_doubles_per_slowdown_multiple() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(250));
entry.record(RunExecution::Serial, Duration::from_micros(100));
for i in 1..(2 * RESAMPLE_INTERVAL) {
assert_eq!(
choose(&mut entry),
(RunExecution::Serial, i % PREFERRED_SAMPLE_INTERVAL == 0)
);
}
assert_eq!(choose(&mut entry), (RunExecution::Parallel, true));
}
#[test]
fn resample_interval_is_capped() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(100));
entry.record(RunExecution::Serial, Duration::from_micros(900));
let interval = RESAMPLE_INTERVAL << MAX_RESAMPLE_SHIFT;
for i in 1..interval {
assert_eq!(
choose(&mut entry),
(RunExecution::Parallel, i % PREFERRED_SAMPLE_INTERVAL == 0)
);
}
assert_eq!(choose(&mut entry), (RunExecution::Serial, true));
}
#[test]
fn recovers_from_poisoned_estimate() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_millis(2));
entry.record(RunExecution::Serial, Duration::from_millis(1));
assert_eq!(
RunEntry::preferred(
entry.serial_ns.get().unwrap(),
entry.parallel_ns.get().unwrap(),
PARALLELISM
),
RunExecution::Serial
);
let mut probes = 0;
let mut flipped_at = None;
for i in 1..=1_000 {
if RunEntry::preferred(
entry.serial_ns.get().unwrap(),
entry.parallel_ns.get().unwrap(),
PARALLELISM,
) == RunExecution::Parallel
{
flipped_at = Some(i - 1);
break;
}
let (execution, measure) = choose(&mut entry);
match execution {
RunExecution::Parallel => {
assert!(measure);
probes += 1;
entry.record(RunExecution::Parallel, Duration::from_micros(500));
}
RunExecution::Serial => {
if measure {
entry.record(RunExecution::Serial, Duration::from_millis(1));
}
}
}
}
assert_eq!(probes, 5);
assert_eq!(flipped_at, Some(600));
}
#[test]
fn seed_offered_once_per_interval() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(250));
for round in 0..2 {
for i in 1..RESAMPLE_INTERVAL {
assert_eq!(
choose(&mut entry),
(RunExecution::Parallel, i % PREFERRED_SAMPLE_INTERVAL == 0),
"round {round}"
);
}
assert_eq!(choose(&mut entry), (RunExecution::Serial, true));
}
entry.record(RunExecution::Serial, Duration::from_millis(1));
assert_eq!(choose(&mut entry).0, RunExecution::Parallel);
}
#[test]
fn probes_big_serial_when_projection_is_affordable() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(500));
entry.record(RunExecution::Serial, Duration::from_millis(15));
assert_eq!(
RunEntry::preferred(
entry.serial_ns.get().unwrap(),
entry.parallel_ns.get().unwrap(),
PARALLELISM
),
RunExecution::Parallel
);
let interval = RESAMPLE_INTERVAL << MAX_RESAMPLE_SHIFT;
for i in 1..interval {
assert_eq!(
choose(&mut entry),
(RunExecution::Parallel, i % PREFERRED_SAMPLE_INTERVAL == 0)
);
}
assert_eq!(choose(&mut entry), (RunExecution::Serial, true));
entry.record(RunExecution::Serial, Duration::from_micros(300));
assert_eq!(entry.serial_ns.get(), Some(12_060_000));
}
#[test]
fn poisoned_probe_cannot_flip_preference() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(800));
entry.record(RunExecution::Serial, Duration::from_millis(3));
assert_eq!(
RunEntry::preferred(
entry.serial_ns.get().unwrap(),
entry.parallel_ns.get().unwrap(),
PARALLELISM
),
RunExecution::Parallel
);
entry.record(RunExecution::Serial, Duration::from_micros(20));
assert_eq!(entry.serial_ns.get(), Some(2_404_000));
assert_eq!(
RunEntry::preferred(
entry.serial_ns.get().unwrap(),
entry.parallel_ns.get().unwrap(),
PARALLELISM
),
RunExecution::Parallel
);
}
#[test]
fn refreshes_preferred_parallel_sample() {
let mut entry = RunEntry::default();
entry.record(RunExecution::Parallel, Duration::from_micros(100));
entry.record(RunExecution::Serial, Duration::from_micros(410));
for i in 1..PREFERRED_SAMPLE_INTERVAL {
assert_eq!(
choose(&mut entry),
(RunExecution::Parallel, i % PREFERRED_SAMPLE_INTERVAL == 0)
);
}
assert_eq!(choose(&mut entry), (RunExecution::Parallel, true));
}
#[test]
fn try_run_records_success_not_errors() {
let policy = Policy::default();
let location = Location::caller();
let len = 10;
let work = 10;
let result: Result<(), ()> = policy.try_run(location, len, work, PARALLELISM, |_| Err(()));
assert!(result.is_err());
let (serial_ns, parallel_ns) = policy.get_entry(location, len, work, PARALLELISM).unwrap();
assert!(serial_ns.is_none() && parallel_ns.is_none());
let result: Result<(), ()> = policy.try_run(location, len, work, PARALLELISM, |_| Ok(()));
assert!(result.is_ok());
let (serial_ns, parallel_estimate) =
policy.get_entry(location, len, work, PARALLELISM).unwrap();
assert!(parallel_estimate.is_some());
assert!(serial_ns.is_none());
for _ in 0..20 {
let _: Result<(), ()> =
policy.try_run(
location,
len,
work,
PARALLELISM,
|execution| match execution {
RunExecution::Parallel => Err(()),
RunExecution::Serial => Ok(()),
},
);
}
let (_, parallel_ns) = policy.get_entry(location, len, work, PARALLELISM).unwrap();
assert_eq!(parallel_ns, parallel_estimate);
}
#[test]
fn spawn_seeds_offload_then_inlines_a_sub_overhead_job() {
let mut entry = SpawnEntry::default();
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Offload, true)
);
entry.job_ns.record(2_000);
entry.overhead_ns.record(50_000);
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Offload, true)
);
entry.job_ns.record(2_000);
entry.overhead_ns.record(50_000);
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Inline, false)
);
}
#[test]
fn spawn_keeps_offloading_a_job_bigger_than_the_overhead() {
let mut entry = SpawnEntry::default();
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Offload, true)
);
entry.job_ns.record(50_000);
entry.overhead_ns.record(5_000);
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Inline, true)
);
entry.inline_ns.record(50_000);
for _ in 0..(2 * RESAMPLE_INTERVAL) {
match entry.choose(SPAWN_INLINE_BUDGET_NS) {
(SpawnExecution::Offload, measure) => {
if measure {
entry.job_ns.record(50_000);
entry.overhead_ns.record(5_000);
}
}
(execution, _) => panic!("expected offload, got {execution:?}"),
}
}
}
#[test]
fn spawn_probe_learns_the_inline_wall() {
let mut entry = SpawnEntry::default();
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Offload, true)
);
entry.job_ns.record(25_000);
entry.overhead_ns.record(10_000);
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Inline, true)
);
entry.inline_ns.record(8_000);
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Inline, false)
);
}
#[test]
fn spawn_budget_caps_inline_when_the_overhead_is_inflated() {
let mut entry = SpawnEntry::default();
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Offload, true)
);
entry.job_ns.record(2_000_000);
entry.overhead_ns.record(50_000_000);
for _ in 0..(2 * RESAMPLE_INTERVAL) {
match entry.choose(SPAWN_INLINE_BUDGET_NS) {
(SpawnExecution::Offload, measure) => {
if measure {
entry.job_ns.record(2_000_000);
entry.overhead_ns.record(50_000_000);
}
}
(execution, _) => panic!("expected offload, got {execution:?}"),
}
}
}
#[test]
fn spawn_single_worker_always_inlines() {
let policy = Policy::default();
let location = Location::caller();
assert_eq!(
policy.choose_spawn(location, 64, 1),
(SpawnExecution::Inline, false)
);
}
#[test]
fn spawn_inline_ewma_crossing_the_overhead_revokes_inline() {
let mut entry = SpawnEntry::default();
entry.job_ns.record(2_000);
entry.overhead_ns.record(50_000);
entry.inline_ns.record(2_000);
assert!(matches!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Inline, _)
));
entry.inline_ns.record(500_000);
assert_eq!(entry.inline_ns.get(), Some(101_600));
assert!(matches!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Offload, _)
));
}
#[test]
fn spawn_entries_bucket_by_len() {
let policy = Policy::default();
let location = Location::caller();
let _ = policy.choose_spawn(location, 1, PARALLELISM);
let _ = policy.choose_spawn(location, 2, PARALLELISM);
let _ = policy.choose_spawn(location, 3, PARALLELISM);
assert_eq!(policy.spawn_len(), 2);
}
#[test]
fn spawn_records_feed_choose() {
let policy = Policy::default();
let location = Location::caller();
assert_eq!(
policy.choose_spawn(location, 64, PARALLELISM),
(SpawnExecution::Offload, true)
);
policy.record_spawn_job(location, 64, PARALLELISM, Duration::from_micros(25));
policy.record_spawn_overhead(location, 64, PARALLELISM, Duration::from_micros(10));
assert_eq!(
policy.choose_spawn(location, 64, PARALLELISM),
(SpawnExecution::Inline, true)
);
policy.record_spawn_inline(location, 64, PARALLELISM, Duration::from_micros(8));
assert_eq!(
policy.choose_spawn(location, 64, PARALLELISM),
(SpawnExecution::Inline, false)
);
}
#[test]
fn spawn_uses_job_ewma() {
let mut entry = SpawnEntry::default();
entry.job_ns.record(2_000);
entry.overhead_ns.record(50_000);
entry.inline_ns.record(2_000);
entry.inline_ns.record(100_000);
assert_eq!(entry.inline_ns.get(), Some(21_600));
assert!(matches!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Inline, _)
));
}
#[test]
fn spawn_recovers_after_a_transient_slow_run() {
let mut entry = SpawnEntry::default();
entry.job_ns.record(2_000);
entry.overhead_ns.record(50_000);
entry.inline_ns.record(15_000_000);
let mut calls: u32 = 0;
loop {
match entry.choose(SPAWN_INLINE_BUDGET_NS) {
(SpawnExecution::Inline, measure) => {
if measure {
entry.inline_ns.record(2_000);
}
if entry.inline_ns.get().unwrap() <= 50_000 {
break;
}
}
(SpawnExecution::Offload, measure) => {
if measure {
entry.job_ns.record(2_000);
entry.overhead_ns.record(50_000);
}
}
}
calls += 1;
assert!(calls <= 100_000, "inline placement never recovered");
}
}
#[test]
fn spawn_inline_records_on_cadence_and_probes_offload() {
let mut entry = SpawnEntry::default();
entry.job_ns.record(30_000);
entry.overhead_ns.record(50_000);
for i in 1..RESAMPLE_INTERVAL {
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(
SpawnExecution::Inline,
i.is_multiple_of(PREFERRED_SAMPLE_INTERVAL)
),
"call {i}"
);
}
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Offload, true)
);
}
#[test]
fn spawn_probe_interval_is_capped() {
let mut entry = SpawnEntry::default();
entry.job_ns.record(2_000);
entry.overhead_ns.record(50_000);
let interval = RESAMPLE_INTERVAL << MAX_RESAMPLE_SHIFT;
for i in 1..interval {
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(
SpawnExecution::Inline,
i.is_multiple_of(PREFERRED_SAMPLE_INTERVAL)
),
"call {i}"
);
}
assert_eq!(
entry.choose(SPAWN_INLINE_BUDGET_NS),
(SpawnExecution::Offload, true)
);
}
}