Require Export rt.util.all. Require Export rt.restructuring.analysis.basic_facts.ideal_schedule. From mathcomp Require Import ssrnat ssrbool fintype. (** In this file, we establish basic facts about the service received by jobs. *) Section Composition. (** To begin with, we provide some simple but handy rewriting rules for [service] and [service_during]. *) (** Consider any job type and any processor state. *) Context {Job: JobType}. Context {PState: Type}. Context `{ProcessorState Job PState}. (** For any given schedule... *) Variable sched: schedule PState. (** ...and any given job... *) Variable j: Job. (** ...we establish a number of useful rewriting rules that decompose the service received during an interval into smaller intervals. *) (** As a trivial base case, no job receives any service during an empty interval. *) Lemma service_during_geq: forall t1 t2, t1 >= t2 -> service_during sched j t1 t2 = 0. Proof. move=> t1 t2 t1t2. rewrite /service_during big_geq //. Qed. (** Equally trivially, no job has received service prior to time zero. *) Corollary service0: service sched j 0 = 0. Proof. rewrite /service service_during_geq //. Qed. (** Trivially, an interval consiting of one time unit is equivalent to service_at. *) Lemma service_during_instant: forall t, service_during sched j t t.+1 = service_at sched j t. Proof. move => t. by rewrite /service_during big_nat_recr ?big_geq //. Qed. (** Next, we observe that we can look at the service received during an interval [t1, t3) as the sum of the service during [t1, t2) and [t2, t3) for any t2 \in [t1, t3]. (The "_cat" suffix denotes the concatenation of the two intervals.) *) Lemma service_during_cat: forall t1 t2 t3, t1 <= t2 <= t3 -> (service_during sched j t1 t2) + (service_during sched j t2 t3) = service_during sched j t1 t3. Proof. move => t1 t2 t3 /andP [t1t2 t2t3]. by rewrite /service_during -big_cat_nat /=. Qed. (** Since [service] is just a special case of [service_during], the same holds for [service]. *) Lemma service_cat: forall t1 t2, t1 <= t2 -> (service sched j t1) + (service_during sched j t1 t2) = service sched j t2. Proof. move=> t1 t2 t1t2. rewrite /service service_during_cat //. Qed. (** As a special case, we observe that the service during an interval can be decomposed into the first instant and the rest of the interval. *) Lemma service_during_first_plus_later: forall t1 t2, t1 < t2 -> (service_at sched j t1) + (service_during sched j t1.+1 t2) = service_during sched j t1 t2. Proof. move => t1 t2 t1t2. have TIMES: t1 <= t1.+1 <= t2 by rewrite /(_ && _) ifT //. have SPLIT := service_during_cat t1 t1.+1 t2 TIMES. by rewrite -service_during_instant //. Qed. (** Symmetrically, we have the same for the end of the interval. *) Lemma service_during_last_plus_before: forall t1 t2, t1 <= t2 -> (service_during sched j t1 t2) + (service_at sched j t2) = service_during sched j t1 t2.+1. Proof. move=> t1 t2 t1t2. rewrite -(service_during_cat t1 t2 t2.+1); last by rewrite /(_ && _) ifT //. rewrite service_during_instant //. Qed. (** And hence also for [service]. *) Corollary service_last_plus_before: forall t, (service sched j t) + (service_at sched j t) = service sched j t.+1. Proof. move=> t. rewrite /service. rewrite -service_during_last_plus_before //. Qed. (** Finally, we deconstruct the service received during an interval [t1, t3) into the service at a midpoint t2 and the service in the intervals before and after. *) Lemma service_split_at_point: forall t1 t2 t3, t1 <= t2 < t3 -> (service_during sched j t1 t2) + (service_at sched j t2) + (service_during sched j t2.+1 t3) = service_during sched j t1 t3. Proof. move => t1 t2 t3 /andP [t1t2 t2t3]. rewrite -addnA service_during_first_plus_later// service_during_cat// /(_ && _) ifT//. by exact: ltnW. Qed. End Composition. Section UnitService. (** As a common special case, we establish facts about schedules in which a job receives either 1 or 0 service units at all times. *) (** Consider any job type and any processor state. *) Context {Job: JobType}. Context {PState: Type}. Context `{ProcessorState Job PState}. (** Let's consider a unit-service model... *) Hypothesis H_unit_service: unit_service_proc_model PState. (** ...and a given schedule. *) Variable sched: schedule PState. (** Let j be any job that is to be scheduled. *) Variable j: Job. (** First, we prove that the instantaneous service cannot be greater than 1, ... *) Lemma service_at_most_one: forall t, service_at sched j t <= 1. Proof. by move=> t; rewrite /service_at. Qed. (** ...which implies that the cumulative service received by job j in any interval of length delta is at most delta. *) Lemma cumulative_service_le_delta: forall t delta, service_during sched j t (t + delta) <= delta. Proof. unfold service_during; intros t delta. apply leq_trans with (n := \sum_(t <= t0 < t + delta) 1); last by rewrite sum_of_ones. by apply: leq_sum => t' _; apply: service_at_most_one. Qed. Section ServiceIsAStepFunction. (** We show that the service received by any job j is a step function. *) Lemma service_is_a_step_function: is_step_function (service sched j). Proof. rewrite /is_step_function => t. rewrite addn1 -service_last_plus_before leq_add2l. apply service_at_most_one. Qed. (** Next, consider any time t... *) Variable t: instant. (** ...and let s0 be any value less than the service received by job j by time t. *) Variable s0: duration. Hypothesis H_less_than_s: s0 < service sched j t. (** Then, we show that there exists an earlier time t0 where job j had s0 units of service. *) Corollary exists_intermediate_service: exists t0, t0 < t /\ service sched j t0 = s0. Proof. feed (exists_intermediate_point (service sched j)); [by apply service_is_a_step_function | intros EX]. feed (EX 0 t); first by done. feed (EX s0); first by rewrite /service /service_during big_geq //. by move: EX => /= [x_mid EX]; exists x_mid. Qed. End ServiceIsAStepFunction. End UnitService. Section Monotonicity. (** We establish a basic fact about the monotonicity of service. *) (** Consider any job type and any processor model. *) Context {Job: JobType}. Context {PState: Type}. Context `{ProcessorState Job PState}. (** Consider any given schedule... *) Variable sched: schedule PState. (** ...and a given job that is to be scheduled. *) Variable j: Job. (** We observe that the amount of service received is monotonic by definition. *) Lemma service_monotonic: forall t1 t2, t1 <= t2 -> service sched j t1 <= service sched j t2. Proof. move=> t1 t2 let1t2. by rewrite -(service_cat sched j t1 t2 let1t2); apply: leq_addr. Qed. End Monotonicity. Section RelationToScheduled. (** Consider any job type and any processor model. *) Context {Job: JobType}. Context {PState: Type}. Context `{ProcessorState Job PState}. (** Consider any given schedule... *) Variable sched: schedule PState. (** ...and a given job that is to be scheduled. *) Variable j: Job. (** We observe that a job that isn't scheduled cannot possibly receive service. *) Lemma not_scheduled_implies_no_service: forall t, ~~ scheduled_at sched j t -> service_at sched j t = 0. Proof. rewrite /service_at /scheduled_at. move=> t NOT_SCHED. rewrite service_implies_scheduled //. Qed. (** Conversely, if a job receives service, then it must be scheduled. *) Lemma service_at_implies_scheduled_at: forall t, service_at sched j t > 0 -> scheduled_at sched j t. Proof. move=> t. destruct (scheduled_at sched j t) eqn:SCHEDULED; first trivial. rewrite not_scheduled_implies_no_service // negbT //. Qed. (** Thus, if the cumulative amount of service changes, then it must be scheduled, too. *) Lemma service_delta_implies_scheduled: forall t, service sched j t < service sched j t.+1 -> scheduled_at sched j t. Proof. move => t. rewrite -service_last_plus_before -{1}(addn0 (service sched j t)) ltn_add2l. apply: service_at_implies_scheduled_at. Qed. (** We observe that a job receives cumulative service during some interval iff it receives services at some specific time in the interval. *) Lemma service_during_service_at: forall t1 t2, service_during sched j t1 t2 > 0 <-> exists t, t1 <= t < t2 /\ service_at sched j t > 0. Proof. split. { move=> NONZERO. case (boolP([exists t: 'I_t2, (t >= t1) && (service_at sched j t > 0)])) => [EX | ALL]. - move: EX => /existsP [x /andP [GE SERV]]. exists x; split => //. apply /andP; by split. - rewrite negb_exists in ALL; move: ALL => /forallP ALL. rewrite /service_during big_nat_cond in NONZERO. rewrite big1 ?ltn0 // in NONZERO => i. rewrite andbT; move => /andP [GT LT]. specialize (ALL (Ordinal LT)); simpl in ALL. rewrite GT andTb -eqn0Ngt in ALL. apply /eqP => //. } { move=> [t [TT SERVICE]]. case (boolP (0 < service_during sched j t1 t2)) => // NZ. exfalso. rewrite -eqn0Ngt in NZ. move/eqP: NZ. rewrite big_nat_eq0 => IS_ZERO. have NO_SERVICE := IS_ZERO t TT. apply lt0n_neq0 in SERVICE. by move/neqP in SERVICE; contradiction. } Qed. (** Thus, any job that receives some service during an interval must be scheduled at some point during the interval... *) Corollary cumulative_service_implies_scheduled: forall t1 t2, service_during sched j t1 t2 > 0 -> exists t, t1 <= t < t2 /\ scheduled_at sched j t. Proof. move=> t1 t2. rewrite service_during_service_at. move=> [t' [TIMES SERVICED]]. exists t'; split => //. by apply: service_at_implies_scheduled_at. Qed. (** ...which implies that any job with positive cumulative service must have been scheduled at some point. *) Corollary positive_service_implies_scheduled_before: forall t, service sched j t > 0 -> exists t', (t' < t /\ scheduled_at sched j t'). Proof. move=> t2. rewrite /service => NONZERO. have EX_SCHED := cumulative_service_implies_scheduled 0 t2 NONZERO. by move: EX_SCHED => [t [TIMES SCHED_AT]]; exists t; split. Qed. Section GuaranteedService. (** If we can assume that a scheduled job always receives service, we can further prove the converse. *) (** Assume j always receives some positive service. *) Hypothesis H_scheduled_implies_serviced: ideal_progress_proc_model PState. (** In other words, not being scheduled is equivalent to receiving zero service. *) Lemma no_service_not_scheduled: forall t, ~~ scheduled_at sched j t <-> service_at sched j t = 0. Proof. move=> t. rewrite /scheduled_at /service_at. split => [NOT_SCHED | NO_SERVICE]. - by rewrite service_implies_scheduled //. - apply (contra (H_scheduled_implies_serviced j (sched t))). by rewrite -eqn0Ngt; apply /eqP => //. Qed. (** Then, if a job does not receive any service during an interval, it is not scheduled. *) Lemma no_service_during_implies_not_scheduled: forall t1 t2, service_during sched j t1 t2 = 0 -> forall t, t1 <= t < t2 -> ~~ scheduled_at sched j t. Proof. move=> t1 t2 ZERO_SUM t /andP [GT_t1 LT_t2]. rewrite no_service_not_scheduled. move: ZERO_SUM. rewrite /service_during big_nat_eq0 => IS_ZERO. by apply (IS_ZERO t); apply /andP; split => //. Qed. (** If a job is scheduled at some point in an interval, it receivees positive cumulative service during the interval... *) Lemma scheduled_implies_cumulative_service: forall t1 t2, (exists t, t1 <= t < t2 /\ scheduled_at sched j t) -> service_during sched j t1 t2 > 0. Proof. move=> t1 t2 [t' [TIMES SCHED]]. rewrite service_during_service_at. exists t'; split => //. move: SCHED. rewrite /scheduled_at /service_at. by apply (H_scheduled_implies_serviced j (sched t')). Qed. (** ...which again applies to total service, too. *) Corollary scheduled_implies_nonzero_service: forall t, (exists t', t' < t /\ scheduled_at sched j t') -> service sched j t > 0. Proof. move=> t [t' [TT SCHED]]. rewrite /service. apply scheduled_implies_cumulative_service. exists t'; split => //. Qed. End GuaranteedService. Section AfterArrival. (** Futhermore, if we know that jobs are not released early, then we can narrow the interval during which they must have been scheduled. *) Context `{JobArrival Job}. (** Assume that jobs must arrive to execute. *) Hypothesis H_jobs_must_arrive: jobs_must_arrive_to_execute sched. (** We prove that any job with positive cumulative service at time [t] must have been scheduled some time since its arrival and before time [t]. *) Lemma positive_service_implies_scheduled_since_arrival: forall t, service sched j t > 0 -> exists t', (job_arrival j <= t' < t /\ scheduled_at sched j t'). Proof. move=> t SERVICE. have EX_SCHED := positive_service_implies_scheduled_before t SERVICE. inversion EX_SCHED as [t'' [TIMES SCHED_AT]]. exists t''; split; last by assumption. rewrite /(_ && _) ifT //. move: H_jobs_must_arrive. rewrite /jobs_must_arrive_to_execute /has_arrived => ARR. by apply: ARR; exact. Qed. Lemma not_scheduled_before_arrival: forall t, t < job_arrival j -> ~~ scheduled_at sched j t. Proof. move=> t EARLY. apply: (contra (H_jobs_must_arrive j t)). rewrite /has_arrived -ltnNge //. Qed. (** We show that job j does not receive service at any time t prior to its arrival. *) Lemma service_before_job_arrival_zero: forall t, t < job_arrival j -> service_at sched j t = 0. Proof. move=> t NOT_ARR. rewrite not_scheduled_implies_no_service // not_scheduled_before_arrival //. Qed. (** Note that the same property applies to the cumulative service. *) Lemma cumulative_service_before_job_arrival_zero : forall t1 t2 : instant, t2 <= job_arrival j -> service_during sched j t1 t2 = 0. Proof. move=> t1 t2 EARLY. rewrite /service_during. have ZERO_SUM: \sum_(t1 <= t < t2) service_at sched j t = \sum_(t1 <= t < t2) 0; last by rewrite ZERO_SUM sum0. rewrite big_nat_cond [in RHS]big_nat_cond. apply: eq_bigr => i /andP [TIMES _]. move: TIMES => /andP [le_t1_i lt_i_t2]. apply (service_before_job_arrival_zero i). by apply leq_trans with (n := t2); auto. Qed. (** Hence, one can ignore the service received by a job before its arrival time... *) Lemma ignore_service_before_arrival: forall t1 t2, t1 <= job_arrival j -> t2 >= job_arrival j -> service_during sched j t1 t2 = service_during sched j (job_arrival j) t2. Proof. move=> t1 t2 le_t1 le_t2. rewrite -(service_during_cat sched j t1 (job_arrival j) t2). rewrite cumulative_service_before_job_arrival_zero //. by apply/andP; split; exact. Qed. (** ... which we can also state in terms of cumulative service. *) Corollary no_service_before_arrival: forall t, t <= job_arrival j -> service sched j t = 0. Proof. move=> t EARLY. rewrite /service cumulative_service_before_job_arrival_zero //. Qed. End AfterArrival. Section TimesWithSameService. (** In this section, we prove some lemmas about time instants with same service. *) (** Consider any time instants t1 and t2... *) Variable t1 t2: instant. (** ...where t1 is no later than t2... *) Hypothesis H_t1_le_t2: t1 <= t2. (** ...and where job j has received the same amount of service. *) Hypothesis H_same_service: service sched j t1 = service sched j t2. (** First, we observe that this means that the job receives no service during [t1, t2)... *) Lemma constant_service_implies_no_service_during: service_during sched j t1 t2 = 0. Proof. move: H_same_service. rewrite -(service_cat sched j t1 t2) // -[service sched j t1 in LHS]addn0 => /eqP. by rewrite eqn_add2l => /eqP //. Qed. (** ...which of course implies that it does not receive service at any point, either. *) Lemma constant_service_implies_not_scheduled: forall t, t1 <= t < t2 -> service_at sched j t = 0. Proof. move=> t /andP [GE_t1 LT_t2]. move: constant_service_implies_no_service_during. rewrite /service_during big_nat_eq0 => IS_ZERO. apply IS_ZERO. apply /andP; split => //. Qed. (** We show that job j receives service at some point t < t1 iff j receives service at some point t' < t2. *) Lemma same_service_implies_serviced_at_earlier_times: [exists t: 'I_t1, service_at sched j t > 0] = [exists t': 'I_t2, service_at sched j t' > 0]. Proof. apply /idP/idP => /existsP [t SERVICED]. { have LE: t < t2 by apply: (leq_trans _ H_t1_le_t2) => //. by apply /existsP; exists (Ordinal LE). } { case (boolP (t < t1)) => LE. - by apply /existsP; exists (Ordinal LE). - exfalso. have TIMES: t1 <= t < t2 by apply /andP; split => //; rewrite leqNgt //. have NO_SERVICE := constant_service_implies_not_scheduled t TIMES. by rewrite NO_SERVICE in SERVICED. } Qed. (** Then, under the assumption that scheduled jobs receives service, we can translate this into a claim about scheduled_at. *) (** Assume j always receives some positive service. *) Hypothesis H_scheduled_implies_serviced: ideal_progress_proc_model PState. (** We show that job j is scheduled at some point t < t1 iff j is scheduled at some point t' < t2. *) Lemma same_service_implies_scheduled_at_earlier_times: [exists t: 'I_t1, scheduled_at sched j t] = [exists t': 'I_t2, scheduled_at sched j t']. Proof. have CONV: forall B, [exists b: 'I_B, scheduled_at sched j b] = [exists b: 'I_B, service_at sched j b > 0]. { move=> B. apply/idP/idP => /existsP [b P]; apply /existsP; exists b. - by move: P; rewrite /scheduled_at /service_at; apply (H_scheduled_implies_serviced j (sched b)). - by apply service_at_implies_scheduled_at. } rewrite !CONV. apply same_service_implies_serviced_at_earlier_times. Qed. End TimesWithSameService. End RelationToScheduled. Require Import rt.restructuring.model.processor.ideal. (** * Incremental Service in Ideal Schedule *) (** In the following section we prove a few facts about service in ideal schedeule. *) (* Note that these lemmas can be generalized to an arbitrary scheduler. *) Section IncrementalService. (** Consider any job type, ... *) Context {Job : JobType}. Context `{JobArrival Job}. Context `{JobCost Job}. (** ... any arrival sequence, ... *) Variable arr_seq : arrival_sequence Job. (** ... and any ideal uniprocessor schedule of this arrival sequence. *) Variable sched : schedule (ideal.processor_state Job). (** As a base case, we prove that if a job j receives service in some time interval [t1,t2), then there exists a time instant t ∈ [t1,t2) such that j is scheduled at time t and t is the first instant where j receives service. *) Lemma positive_service_during: forall j t1 t2, 0 < service_during sched j t1 t2 -> exists t : nat, t1 <= t < t2 /\ scheduled_at sched j t /\ service_during sched j t1 t = 0. Proof. intros j t1 t2 SERV. have LE: t1 <= t2. { rewrite leqNgt; apply/negP; intros CONTR. by apply ltnW in CONTR; move: SERV; rewrite /service_during big_geq. } destruct (scheduled_at sched j t1) eqn:SCHED. { exists t1; repeat split; try done. - apply/andP; split; first by done. rewrite ltnNge; apply/negP; intros CONTR. by move: SERV; rewrite/service_during big_geq. - by rewrite /service_during big_geq. } { apply negbT in SCHED. move: SERV; rewrite /service /service_during; move => /sum_seq_gt0P [t [IN SCHEDt]]. rewrite lt0b in SCHEDt. rewrite mem_iota subnKC in IN; last by done. move: IN => /andP [IN1 IN2]. move: (exists_first_intermediate_point ((fun t => scheduled_at sched j t)) t1 t IN1 SCHED) => A. feed A; first by rewrite scheduled_at_def/=. move: A => [x [/andP [T1 T4] [T2 T3]]]. exists x; repeat split; try done. - apply/andP; split; first by apply ltnW. by apply leq_ltn_trans with t. - apply/eqP; rewrite big_nat_cond big1 //. move => y /andP [T5 _]. by apply/eqP; rewrite eqb0; specialize (T2 y); rewrite scheduled_at_def/= in T2; apply T2. } Qed. (** Next, we prove that if in some time interval [t1,t2) a job j receives k units of service, then there exists a time instant t ∈ [t1,t2) such that j is scheduled at time t and service of job j within interval [t1,t) is equal to k. *) Lemma incremental_service_during: forall j t1 t2 k, service_during sched j t1 t2 > k -> exists t, t1 <= t < t2 /\ scheduled_at sched j t /\ service_during sched j t1 t = k. Proof. intros j t1 t2 k SERV. have LE: t1 <= t2. { rewrite leqNgt; apply/negP; intros CONTR. by apply ltnW in CONTR; move: SERV; rewrite /service_during big_geq. } induction k; first by apply positive_service_during in SERV. feed IHk; first by apply ltn_trans with k.+1. move: IHk => [t [/andP [NEQ1 NEQ2] [SCHEDt SERVk]]]. have SERVk1: service_during sched j t1 t.+1 = k.+1. { rewrite -(service_during_cat _ _ _ t); last by apply/andP; split. rewrite SERVk -[X in _ = X]addn1; apply/eqP; rewrite eqn_add2l. by rewrite /service_during big_nat1 /service_at eqb1 -scheduled_at_def/=. } move: SERV; rewrite -(service_during_cat _ _ _ t.+1); last first. { by apply/andP; split; first apply leq_trans with t. } rewrite SERVk1 -addn1 leq_add2l; move => SERV. destruct (scheduled_at sched j t.+1) eqn:SCHED. - exists t.+1; repeat split; try done. apply/andP; split. + apply leq_trans with t; by done. + rewrite ltnNge; apply/negP; intros CONTR. by move: SERV; rewrite /service_during big_geq. - apply negbT in SCHED. move: SERV; rewrite /service /service_during; move => /sum_seq_gt0P [x [INx SCHEDx]]. rewrite lt0b in SCHEDx. rewrite mem_iota subnKC in INx; last by done. move: INx => /andP [INx1 INx2]. move: (exists_first_intermediate_point _ _ _ INx1 SCHED) => A. feed A; first by rewrite scheduled_at_def/=. move: A => [y [/andP [T1 T4] [T2 T3]]]. exists y; repeat split; try done. + apply/andP; split. apply leq_trans with t; first by done. apply ltnW, ltn_trans with t.+1; by done. by apply leq_ltn_trans with x. + rewrite (@big_cat_nat _ _ _ t.+1) //=; [ | by apply leq_trans with t | by apply ltn_trans with t.+1]. unfold service_during in SERVk1; rewrite SERVk1; apply/eqP. rewrite -{2}[k.+1]addn0 eqn_add2l. rewrite big_nat_cond big1 //; move => z /andP [H5 _]. by apply/eqP; rewrite eqb0; specialize (T2 z); rewrite scheduled_at_def/= in T2; apply T2. Qed. End IncrementalService.