///|
/// Scheduling controls for deterministic execution planning.
pub(all) struct SchedulePolicy {
datum_first : Bool
group_by_device : Bool
max_batch_size : Int
}
///|
/// A scheduled plan with operational cost and batches.
pub(all) struct ScheduledPlan {
part_id : String
steps : Array[MeasurementStep]
phases : Array[ExecutionPhase]
batches : Array[Array[MeasurementStep]]
phase_count : Int
device_switches : Int
estimated_seconds : Double
}
///|
/// Return a conservative default scheduling policy.
pub fn default_schedule_policy() -> SchedulePolicy {
{ datum_first: true, group_by_device: true, max_batch_size: 16 }
}
///|
/// Construct an explicit scheduling policy.
pub fn schedule_policy(
datum_first~ : Bool,
group_by_device~ : Bool,
max_batch_size~ : Int,
) -> SchedulePolicy {
{ datum_first, group_by_device, max_batch_size }
}
///|
fn schedule_steps(
plan : MeasurementPlan,
policy : SchedulePolicy,
) -> Array[MeasurementStep] {
let ordered = if policy.datum_first {
execution_order(plan)
} else {
plan.steps
}
if policy.group_by_device {
let grouped_plan = MeasurementPlan::{
part_id: plan.part_id,
steps: ordered,
summary: plan.summary,
}
grouped_execution_order(grouped_plan)
} else {
ordered
}
}
///|
/// Split steps into non-empty fixed-size batches.
pub fn split_steps_into_batches(
steps : Array[MeasurementStep],
max_batch_size : Int,
) -> Array[Array[MeasurementStep]] {
let batches = []
if max_batch_size <= 0 {
batches
} else {
let mut start = 0
while start < steps.length() {
let end = if start + max_batch_size < steps.length() {
start + max_batch_size
} else {
steps.length()
}
let batch = []
for i in start.. ScheduledPlan {
let steps = schedule_steps(plan, policy)
let scheduled_plan = MeasurementPlan::{
part_id: plan.part_id,
steps,
summary: plan.summary,
}
let phases = execution_phases(scheduled_plan)
let batches = split_steps_into_batches(steps, policy.max_batch_size)
{
part_id: plan.part_id,
steps,
phases,
batches,
phase_count: phases.length(),
device_switches: device_switch_count(steps),
estimated_seconds: estimated_execution_seconds(scheduled_plan),
}
}
///|
/// Return whether two executions of the same plan yield identical IDs/order.
pub fn schedule_is_deterministic(plan : MeasurementPlan) -> Bool {
let policy = default_schedule_policy()
let first = schedule_plan(plan, policy)
let second = schedule_plan(plan, policy)
if first.steps.length() != second.steps.length() {
false
} else {
let mut same = true
for i in 0.. Bool {
if max_batch_size <= 0 {
batches.length() == 0
} else {
let mut result = true
for batch in batches {
if batch.length() == 0 || batch.length() > max_batch_size {
result = false
}
}
result
}
}
///|
/// Return all scheduled feature IDs.
pub fn scheduled_feature_ids(schedule : ScheduledPlan) -> Array[String] {
let result = []
for step in schedule.steps {
result.push(step.feature_id)
}
result
}
///|
/// Return whether the schedule contains a datum phase.
pub fn schedule_establishes_datum(schedule : ScheduledPlan) -> Bool {
let mut result = false
for phase in schedule.phases {
if phase.phase == PlanPhase::EstablishDatum {
result = true
}
}
result
}
///|
/// Estimate total samples across schedule batches.
pub fn scheduled_sample_count(schedule : ScheduledPlan) -> Int {
let mut result = 0
for step in schedule.steps {
match step.strategy {
SinglePoint => result += 1
RepeatedPoint(count~) => result += count
Scan(count~) => result += count
}
}
result
}
///|
/// Return the largest batch size in a schedule.
pub fn largest_batch_size(schedule : ScheduledPlan) -> Int {
let mut result = 0
for batch in schedule.batches {
if batch.length() > result {
result = batch.length()
}
}
result
}