docs: bilingual comments for apsis-schedule
This commit is contained in:
parent
0a221e23b7
commit
175abb3f68
|
|
@ -4,21 +4,33 @@ use apsis_resource::ResourceId;
|
|||
use apsis_task::TaskId;
|
||||
|
||||
/// One task placed on the schedule.
|
||||
///
|
||||
/// 计划上放置的一个任务。
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Placement {
|
||||
/// The task placed.
|
||||
///
|
||||
/// 被放置的任务。
|
||||
pub task: TaskId,
|
||||
/// The start time of the placement, in seconds.
|
||||
///
|
||||
/// 放置的起始时刻,单位为秒。
|
||||
pub start: f64,
|
||||
/// The duration of the placement, in seconds.
|
||||
///
|
||||
/// This is copied from the task so that a schedule carries everything
|
||||
/// needed to evaluate it without the tasks to hand.
|
||||
///
|
||||
/// 放置的时长,单位为秒。
|
||||
///
|
||||
/// 它从任务复制而来,故一份计划无需持有任务集合也能完成评估。
|
||||
pub duration: f64,
|
||||
}
|
||||
|
||||
impl Placement {
|
||||
/// Creates a placement.
|
||||
///
|
||||
/// 创建一次放置。
|
||||
pub const fn new(task: TaskId, start: f64, duration: f64) -> Self {
|
||||
Self {
|
||||
task,
|
||||
|
|
@ -28,6 +40,8 @@ impl Placement {
|
|||
}
|
||||
|
||||
/// Returns the end time of the placement, in seconds.
|
||||
///
|
||||
/// 返回放置的结束时刻,单位为秒。
|
||||
pub fn end(&self) -> f64 {
|
||||
self.start + self.duration
|
||||
}
|
||||
|
|
@ -37,31 +51,50 @@ impl Placement {
|
|||
/// The interval is half-open, so a placement covers its start and not its
|
||||
/// end. That convention lets two back to back placements share a boundary
|
||||
/// without overlapping.
|
||||
///
|
||||
/// 当该放置覆盖某瞬时返回 `true`。
|
||||
///
|
||||
/// 区间是半开的,故放置含其起点而不含其终点。该约定使两个首尾相接的
|
||||
/// 放置可共享边界而不重叠。
|
||||
pub fn covers(&self, time: f64) -> bool {
|
||||
time >= self.start && time < self.end()
|
||||
}
|
||||
|
||||
/// Returns `true` when the placement overlaps another.
|
||||
///
|
||||
/// 当该放置与另一个放置重叠时返回 `true`。
|
||||
pub fn overlaps(&self, other: &Self) -> bool {
|
||||
self.start < other.end() && other.start < self.end()
|
||||
}
|
||||
|
||||
/// Returns `true` when the placement is usable.
|
||||
///
|
||||
/// 当该放置可用时返回 `true`。
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.start.is_finite() && self.duration.is_finite() && self.duration >= 0.0
|
||||
}
|
||||
}
|
||||
|
||||
/// How much of one resource a schedule uses.
|
||||
///
|
||||
/// 一份计划对某个资源的使用情况。
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ResourceUsage {
|
||||
/// The resource.
|
||||
///
|
||||
/// 该资源。
|
||||
pub resource: ResourceId,
|
||||
/// The greatest total demand placed on the resource at any instant.
|
||||
///
|
||||
/// 该资源上任意瞬时的最大总需求。
|
||||
pub peak_demand: f64,
|
||||
/// The time during which the resource is used at all, in seconds.
|
||||
///
|
||||
/// 该资源被使用的总时长,单位为秒。
|
||||
pub busy_seconds: f64,
|
||||
/// The number of placements that use the resource.
|
||||
///
|
||||
/// 使用该资源的放置数量。
|
||||
pub placement_count: usize,
|
||||
}
|
||||
|
||||
|
|
@ -73,6 +106,13 @@ impl ResourceUsage {
|
|||
/// only half occupied by demand still counts as busy.
|
||||
///
|
||||
/// Returns `None` when the interval is not positive.
|
||||
///
|
||||
/// 返回该资源在某区间上的利用率。
|
||||
///
|
||||
/// 其值为忙碌时长除以区间长度。这是时间利用率而非容量利用率:仅被
|
||||
/// 需求占据一半的资源同样算作忙碌。
|
||||
///
|
||||
/// 区间非正时返回 `None`。
|
||||
pub fn utilisation(&self, interval_seconds: f64) -> Option<f64> {
|
||||
if interval_seconds > 0.0 && interval_seconds.is_finite() {
|
||||
Some(self.busy_seconds / interval_seconds)
|
||||
|
|
@ -83,19 +123,33 @@ impl ResourceUsage {
|
|||
}
|
||||
|
||||
/// The evaluation of a whole schedule.
|
||||
///
|
||||
/// 整份计划的评估结果。
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Evaluation {
|
||||
/// The number of placements.
|
||||
///
|
||||
/// 放置的数量。
|
||||
pub placed_count: usize,
|
||||
/// The number of tasks that were offered to the schedule.
|
||||
///
|
||||
/// 提交给该计划的任务数量。
|
||||
pub offered_count: usize,
|
||||
/// The sum of the placed durations, in seconds.
|
||||
///
|
||||
/// 已放置时长之和,单位为秒。
|
||||
pub scheduled_seconds: f64,
|
||||
/// The earliest start time, or zero when nothing is placed.
|
||||
///
|
||||
/// 最早的开始时刻;无放置时为零。
|
||||
pub makespan_start: f64,
|
||||
/// The latest end time, or zero when nothing is placed.
|
||||
///
|
||||
/// 最晚的结束时刻;无放置时为零。
|
||||
pub makespan_end: f64,
|
||||
/// The usage of each resource, in identifier order.
|
||||
///
|
||||
/// 各资源的使用情况,按标识符顺序排列。
|
||||
pub resource_usage: Vec<ResourceUsage>,
|
||||
}
|
||||
|
||||
|
|
@ -105,6 +159,10 @@ impl Evaluation {
|
|||
///
|
||||
/// This is the time from the earliest start to the latest end, which is
|
||||
/// the makespan of the plan.
|
||||
///
|
||||
/// 返回该计划覆盖的规划时域长度,单位为秒。
|
||||
///
|
||||
/// 它是从最早开始到最晚结束的时间,即该计划的 makespan。
|
||||
pub fn makespan(&self) -> f64 {
|
||||
(self.makespan_end - self.makespan_start).max(0.0)
|
||||
}
|
||||
|
|
@ -112,6 +170,10 @@ impl Evaluation {
|
|||
/// Returns the fraction of the offered tasks that were placed.
|
||||
///
|
||||
/// Returns `None` when no tasks were offered.
|
||||
///
|
||||
/// 返回所提交任务中被放置的比例。
|
||||
///
|
||||
/// 没有提交任务时返回 `None`。
|
||||
pub fn fill_rate(&self) -> Option<f64> {
|
||||
if self.offered_count == 0 {
|
||||
return None;
|
||||
|
|
@ -120,11 +182,15 @@ impl Evaluation {
|
|||
}
|
||||
|
||||
/// Returns `true` when every offered task was placed.
|
||||
///
|
||||
/// 当所有提交任务均被放置时返回 `true`。
|
||||
pub fn is_complete(&self) -> bool {
|
||||
self.placed_count == self.offered_count
|
||||
}
|
||||
|
||||
/// Returns the usage of one resource.
|
||||
///
|
||||
/// 返回某个资源的使用情况。
|
||||
pub fn usage_of(&self, resource: ResourceId) -> Option<&ResourceUsage> {
|
||||
self.resource_usage
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ use std::collections::BTreeMap;
|
|||
/// may be placed at most once: placing it again is an error rather than a
|
||||
/// silent overwrite, because a schedule that contains two placements for one
|
||||
/// task is not a schedule.
|
||||
///
|
||||
/// 一组任务放置。
|
||||
///
|
||||
/// 该计划按放置发生的顺序保存各项,并维护从任务到放置的索引,故按任务查找
|
||||
/// 是直接的。一个任务至多放置一次:重复放置是错误而非静默覆盖,因为含有
|
||||
/// 同一任务两处放置的集合已不是一份计划。
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct Schedule {
|
||||
placements: Vec<Placement>,
|
||||
|
|
@ -21,6 +27,8 @@ pub struct Schedule {
|
|||
|
||||
impl Schedule {
|
||||
/// Creates an empty schedule.
|
||||
///
|
||||
/// 创建一份空计划。
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
placements: Vec::new(),
|
||||
|
|
@ -29,26 +37,36 @@ impl Schedule {
|
|||
}
|
||||
|
||||
/// Returns the placements in the order they were made.
|
||||
///
|
||||
/// 返回按放置顺序排列的各项。
|
||||
pub fn placements(&self) -> &[Placement] {
|
||||
&self.placements
|
||||
}
|
||||
|
||||
/// Returns the number of placements.
|
||||
///
|
||||
/// 返回放置的数量。
|
||||
pub fn len(&self) -> usize {
|
||||
self.placements.len()
|
||||
}
|
||||
|
||||
/// Returns `true` when there are no placements.
|
||||
///
|
||||
/// 当没有放置时返回 `true`。
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.placements.is_empty()
|
||||
}
|
||||
|
||||
/// Returns `true` when a task has been placed.
|
||||
///
|
||||
/// 当某任务已被放置时返回 `true`。
|
||||
pub fn is_placed(&self, task: TaskId) -> bool {
|
||||
self.index.contains_key(&task.index())
|
||||
}
|
||||
|
||||
/// Returns the placement of a task.
|
||||
///
|
||||
/// 返回某任务的放置。
|
||||
pub fn placement_of(&self, task: TaskId) -> Option<&Placement> {
|
||||
self.index
|
||||
.get(&task.index())
|
||||
|
|
@ -64,6 +82,14 @@ impl Schedule {
|
|||
///
|
||||
/// Returns an error when the task is already placed, when the start time
|
||||
/// is not finite, or when the placement falls outside the task's window.
|
||||
///
|
||||
/// 把某任务放置在某时刻。
|
||||
///
|
||||
/// 放置会对照该任务的窗口校验。任务的资源通过任务本身随放置一同记录,
|
||||
/// 故本方法无需它们;容量检查由需要资源的
|
||||
/// [`Schedule::validate_capacity`] 完成。
|
||||
///
|
||||
/// 该任务已被放置、起始时刻非有限,或放置落在任务窗口之外时返回错误。
|
||||
pub fn place(
|
||||
&mut self,
|
||||
task: &apsis_task::Task,
|
||||
|
|
@ -107,6 +133,10 @@ impl Schedule {
|
|||
/// Removes the placement of a task.
|
||||
///
|
||||
/// Returns an error when the task is not placed.
|
||||
///
|
||||
/// 移除某任务的放置。
|
||||
///
|
||||
/// 该任务未被放置时返回错误。
|
||||
pub fn unplace(&mut self, task: TaskId) -> Result<Placement, ScheduleError> {
|
||||
let position = self.index.remove(&task.index()).ok_or_else(|| {
|
||||
ScheduleError::new(ScheduleErrorKind::NotPlaced).with_detail(format!("{task}"))
|
||||
|
|
@ -115,11 +145,15 @@ impl Schedule {
|
|||
// Every placement after the removed one shifts down by one, so the
|
||||
// index must be rebuilt. The schedule is small enough that this is
|
||||
// cheaper than maintaining a linked structure.
|
||||
// 被移除项之后的每项都前移一位,故索引须重建。计划规模足够小,
|
||||
// 这比维护链式结构更划算。
|
||||
self.rebuild_index();
|
||||
Ok(removed)
|
||||
}
|
||||
|
||||
/// Rebuilds the task to position index.
|
||||
///
|
||||
/// 重建从任务到位置的索引。
|
||||
fn rebuild_index(&mut self) {
|
||||
self.index.clear();
|
||||
for (position, placement) in self.placements.iter().enumerate() {
|
||||
|
|
@ -128,6 +162,8 @@ impl Schedule {
|
|||
}
|
||||
|
||||
/// Returns the placements that use a resource, in time order.
|
||||
///
|
||||
/// 返回使用某资源的放置,按时间排序。
|
||||
pub fn placements_on(
|
||||
&self,
|
||||
resource: ResourceId,
|
||||
|
|
@ -159,6 +195,12 @@ impl Schedule {
|
|||
/// tasks on disjoint resources may legitimately overlap, so a caller that
|
||||
/// wants to know whether a resource is overbooked should use
|
||||
/// [`Schedule::validate_capacity`] instead.
|
||||
///
|
||||
/// 当两个不同任务的放置重叠时返回 `true`。
|
||||
///
|
||||
/// 这是计划与自身的比较,忽略资源。位于不相交资源上的两个任务本就可
|
||||
/// 以合法重叠,故欲知某资源是否超订的调用方应改用
|
||||
/// [`Schedule::validate_capacity`]。
|
||||
pub fn has_overlapping_placements(&self) -> bool {
|
||||
for (index, placement) in self.placements.iter().enumerate() {
|
||||
if self.placements[..index]
|
||||
|
|
@ -172,6 +214,8 @@ impl Schedule {
|
|||
}
|
||||
|
||||
/// Returns the total placed duration, in seconds.
|
||||
///
|
||||
/// 返回已放置的总时长,单位为秒。
|
||||
pub fn scheduled_seconds(&self) -> f64 {
|
||||
self.placements
|
||||
.iter()
|
||||
|
|
@ -182,6 +226,10 @@ impl Schedule {
|
|||
/// Returns the earliest start of any placement.
|
||||
///
|
||||
/// Returns `None` when the schedule is empty.
|
||||
///
|
||||
/// 返回所有放置中最早的开始时刻。
|
||||
///
|
||||
/// 计划为空时返回 `None`。
|
||||
pub fn earliest_start(&self) -> Option<f64> {
|
||||
self.placements
|
||||
.iter()
|
||||
|
|
@ -196,6 +244,10 @@ impl Schedule {
|
|||
/// Returns the latest end of any placement.
|
||||
///
|
||||
/// Returns `None` when the schedule is empty.
|
||||
///
|
||||
/// 返回所有放置中最晚的结束时刻。
|
||||
///
|
||||
/// 计划为空时返回 `None`。
|
||||
pub fn latest_end(&self) -> Option<f64> {
|
||||
self.placements
|
||||
.iter()
|
||||
|
|
@ -219,6 +271,17 @@ impl Schedule {
|
|||
/// between boundaries.
|
||||
///
|
||||
/// Returns an error describing the first violation.
|
||||
///
|
||||
/// 对照一组资源校验该计划。
|
||||
///
|
||||
/// 检查分两部分。其一,每处放置须与其任务的需求一致,这可捕获用另
|
||||
/// 一份任务列表建成的计划。其二,对每个资源,任意瞬时的总需求不得超
|
||||
/// 过容量。
|
||||
///
|
||||
/// 第二部分按时间序遍历各资源的放置边界,由于相邻边界之间总需求恒定,
|
||||
/// 该遍历是精确的。
|
||||
///
|
||||
/// 返回描述首个违规的错误。
|
||||
pub fn validate_capacity(
|
||||
&self,
|
||||
tasks: &[apsis_task::Task],
|
||||
|
|
@ -243,6 +306,7 @@ impl Schedule {
|
|||
|
||||
for resource in resources.resources() {
|
||||
// The boundaries at which the total demand can change.
|
||||
// 总需求可能发生变化的各边界。
|
||||
let mut events: Vec<(f64, f64)> = Vec::new();
|
||||
for placement in &self.placements {
|
||||
let Some(task) = tasks.iter().find(|task| task.id == placement.task) else {
|
||||
|
|
@ -259,6 +323,8 @@ impl Schedule {
|
|||
// Sort by time, with a release before an acquisition at the same
|
||||
// instant so that two placements that touch are not counted as
|
||||
// overlapping.
|
||||
// 按时间排序;同一瞬先释放后获取,使两个相接的放置不被计为
|
||||
// 重叠。
|
||||
events.sort_by(|first, second| {
|
||||
first
|
||||
.0
|
||||
|
|
@ -294,6 +360,11 @@ impl Schedule {
|
|||
/// The evaluation reports the counts, the makespan, and the usage of each
|
||||
/// resource. The peak demand of a resource is found by the same event
|
||||
/// sweep that the capacity check uses, so the two agree.
|
||||
///
|
||||
/// 对照任务列表与资源集合评估该计划。
|
||||
///
|
||||
/// 评估给出各项计数、makespan 以及每个资源的使用情况。资源的峰值
|
||||
/// 需求由与容量检查相同的事件扫掠求得,故二者一致。
|
||||
pub fn evaluate(
|
||||
&self,
|
||||
tasks: &[apsis_task::Task],
|
||||
|
|
@ -364,6 +435,11 @@ impl Schedule {
|
|||
/// A schedule is stored in the order it was built, which is the order a
|
||||
/// scheduler considered the tasks. A caller that wants to read the plan
|
||||
/// in time order uses this.
|
||||
///
|
||||
/// 返回按开始时刻排序的计划副本,原计划不变。
|
||||
///
|
||||
/// 计划按其构建顺序存储,即调度器考虑任务的顺序。欲按时间阅读该计
|
||||
/// 划的调用方使用本方法。
|
||||
pub fn in_time_order(&self) -> Vec<Placement> {
|
||||
let mut ordered = self.placements.clone();
|
||||
ordered.sort_by(|first, second| {
|
||||
|
|
@ -381,6 +457,11 @@ impl Schedule {
|
|||
///
|
||||
/// Returns `None` when the resource is idle there. This is what a
|
||||
/// sequential scheduler asks when it looks for the next gap.
|
||||
///
|
||||
/// 若某资源在给定瞬时忙碌,返回其变为空闲的时刻。
|
||||
///
|
||||
/// 该资源在彼处空闲时返回 `None`。顺序调度器寻找下一个空隙时正是
|
||||
/// 询问这一点。
|
||||
pub fn resource_busy_until(
|
||||
&self,
|
||||
resource: ResourceId,
|
||||
|
|
|
|||
Loading…
Reference in New Issue