# Lecture 3 - Scheduling Source: https://wisconsin.twango.dev/sp26-cs537/lectures/lecture-03 # Lecture 3 - Scheduling **Instructor:** Tej Chajed **Topics:** Scheduling policies, metrics, FIFO, SJF, STCF, Round Robin, MLFQ, Lottery Scheduling > [!abstract]- Learning Outcomes > - Compare different scheduling policies based on several metrics > - Articulate the challenges from interactive and batch processes > - Articulate the challenge of scheduling under incomplete info --- ## Recap: Process vs Program - **Program** is passive — code + data on disk - **Process** is a running program — stack, registers, program counter Can have multiple process instances of the same program (e.g., two users both running Firefox — same program, separate processes). ### Process State Transitions ```mermaid stateDiagram-v2 New --> Ready : create Ready --> Running : schedule Running --> Ready : unschedule Running --> blocked : I/O, page fault, etc. blocked --> Ready : I/O done Running --> Terminated : kill ``` ### System Calls - Process invokes `syscall` instruction, which looks up handler in a table and jumps to kernel code - Kernel can access user memory to fill in user buffer - `return-from-trap` at end returns control to the process --- ## Scheduling Vocabulary > **Workload:** A set of jobs, each with an arrival time and run time. > **Job:** Current execution of a process. Alternates between CPU and I/O, moves between ready and blocked queues. Also called a "task." > **Scheduler:** Decides which ready job to run. > **Metric:** A measurement of scheduling quality. --- ## Scheduling Metrics ### Turnaround Time > **Turnaround time** = *completion_time* - *arrival_time* > [!example] Example > Process A arrives at t = 10, finishes t = 30 → turnaround = 20 > Process B arrives at t = 10, finishes t = 50 → turnaround = 40 > Average turnaround = 30 ### Response Time > **Response time** = *first_run_time* - *arrival_time* Valuable for **interactive processes** — measures how quickly a process first gets the CPU after arriving. > [!example] Example > A runs from 0–20, B arrives at 20 and runs 20–30. > B's turnaround: 20s. B's response: 10s. ### Starvation > **Starvation:** A process is prevented from making progress because another process holds a resource it needs. A poor scheduling policy can cause starvation — e.g., if an important process always prevents a less important process from running on the CPU. --- ## Approach Start with simplifying **assumptions**, pick a **metric**, then design a **scheduling policy**. Progressively relax assumptions to reveal challenges. ### Initial Assumptions 1. Each job runs for the same amount of time 2. All jobs arrive at the same time 3. All jobs only use the CPU (no I/O) 4. Run-time of each job is known --- ## FIFO / FCFS (First Come, First Served) Run jobs in the order they arrive. > [!example] Example — Equal-Length Jobs > > | Job | Arrival (s) | Run time (s) | Turnaround (s) | > |-----|-------------|--------------|-----------------| > | A | ~0 | 10 | 10 | > | B | ~0 | 10 | 20 | > | C | ~0 | 10 | 30 | > > Average turnaround time = (10 + 20 + 30) / 3 = **20s** ### Problem: Variable-Length Jobs (Convoy Effect) Relax assumption 1 (jobs have different run times): > [!example] Example — Variable-Length Jobs > > | Job | Arrival (s) | Run time (s) | > |-----|-------------|--------------| > | A | ~0 | 100 | > | B | ~0 | 10 | > | C | ~0 | 10 | > > FIFO schedule: A → B → C > Average turnaround = (100 + 110 + 120) / 3 = **110s** Turnaround time suffers when short jobs must wait for long jobs — **head-of-line blocking**. --- ## Shortest Job First (SJF) Choose the job with the **smallest run time** first. > [!example] Example — All Arrive at Same Time > > | Job | Arrival (s) | Run time (s) | Turnaround (s) | > |-----|-------------|--------------|-----------------| > | A | ~0 | 100 | 120 | > | B | ~0 | 10 | 10 | > | C | ~0 | 10 | 20 | > > SJF schedule: B → C → A > Average turnaround = (10 + 20 + 120) / 3 = **50s** ### Problem: Late Arrivals Relax assumption 2 (jobs can arrive at different times). SJF is **non-preemptive** — once a job starts, it runs to completion. > [!example] Example — Different Arrival Times > > | Job | Arrival (s) | Run time (s) | > |-----|-------------|--------------| > | A | ~0 | 100 | > | B | 10 | 10 | > | C | 10 | 10 | > > SJF schedule: A runs first (only job at t=0), then B → C > Average turnaround = (100 + 100 + 110) / 3 = **103.3s** --- ## Preemptive Scheduling: STCF Previous schedulers (FIFO, SJF) are **non-preemptive** — they only schedule a new job when the previous job voluntarily relinquishes the CPU. > **Preemption:** Schedule a different job by taking the CPU away from a *running* job. > [!note] Lecture Note > Preemption is a **mechanism**, not a policy. ### Shortest Time-to-Completion First (STCF) Always schedule the job with the **minimum time remaining**. > [!example] Example > > | Job | Arrival (s) | Run time (s) | > |-----|-------------|--------------| > | A | ~0 | 100 | > | B | 10 | 10 | > | C | 10 | 10 | > > At time 10, B and C arrive. Remaining times: A = 90s, B = 10s, C = 10s. > STCF preempts A, runs B (10–20), then C (20–30), then A resumes (30–120). > Average turnaround = (10 + 20 + 120) / 3 = **50s** --- ## Round Robin (RR) **Key idea:** Switch between jobs periodically (using a **time slice**) to reduce response time. > [!example] Example — Three Equal Jobs (5s each, all arrive at ~0) > > **FCFS:** A (0–5), B (5–10), C (10–15) > - Average response time = (0 + 5 + 10) / 3 = **5s** > - Average turnaround time = (5 + 10 + 15) / 3 = **10s** > > **Round Robin (1s slice):** A, B, C, A, B, C, A, B, C, ... > - Average response time = (0 + 1 + 2) / 3 = **1s** > - Average turnaround time = (13 + 14 + 15) / 3 = **14s** ### Trade-offs - Round robin **increases turnaround time** but **decreases response time** - Tuning challenges: - What is a good time slice length? - What is the overhead of context switching? > [!note] Lecture Note > Context switch overhead is ~50μs. The time slice must be long enough to amortize this cost but short enough for good response time. --- ## I/O-Aware Scheduling Relax assumption 3 (jobs can perform I/O). ### Not I/O Aware If a job holds the CPU while blocked on disk, the CPU sits idle — wasteful. ### I/O-Aware Approach Treat each CPU burst of a job as a separate "sub-job." When a job completes I/O, its new CPU burst enters the ready queue. > [!example] Example > Job A alternates between 10s CPU bursts and disk I/O. Job B needs 50s of CPU. > With STCF, each of A's CPU bursts (10s) is shorter than B's remaining time, so A preempts B whenever A completes I/O. Both CPU and disk stay busy. --- ## Multi-Level Feedback Queue (MLFQ) Relax assumption 4 (run time is **not** known). Must support two job types: - **Interactive** programs care about **response time** - **Batch** programs care about **turnaround time** ### Structure - Multiple levels of round-robin queues - Each level has **higher priority** than the level below - Higher-priority jobs can **preempt** lower-priority jobs ### MLFQ Rules - **Rule 1:** If `priority(A) > priority(B)`, A runs - **Rule 2:** If `priority(A) == priority(B)`, A & B run in round robin - **Rule 3:** New processes start at the **top priority** - **Rule 4:** If a job uses its whole time slice, **demote** it to a lower priority queue > [!note] Lecture Note > Rule 4 is the "feedback" — the scheduler uses past behavior to predict future behavior. Longer time slices are given at lower priorities. ### One Long Job A long-running job starts at Q2 (top), gets demoted to Q1 after using its time slice, then demoted to Q0 where it runs with the longest time slice. ### Interactive Process Joins When a new interactive process B arrives, it enters at Q2 (top priority) and preempts the long-running job A sitting in Q0. ### Problems with Basic MLFQ - **Starvation:** If many interactive jobs (B, C) keep arriving at Q2 and alternate between I/O and CPU, the long-running job A in Q0 never gets to run ### Avoiding Starvation: Priority Boost - **Rule 5:** After some time period S, move **all** jobs in the system to the topmost queue > [!note] Lecture Note > Each queue has its own time slice length. The boost periodically resets all priorities, giving long-running jobs a chance to run. --- ## Lottery Scheduling MLFQ is not fair — shorter burst jobs get more CPU time. **Goal:** Proportional (fair) share — e.g., 3 users each get 1/3 of CPU, no matter how long they run. ### Approach - Give processes **lottery tickets** - At each scheduling decision, draw a random winner — that process runs - **Higher priority = more tickets** = higher proportion of CPU ### Example | Job | Tickets | |-----|---------| | A | 10 | | B | 20 | | C | 30 | - Lotteries control proportion of CPU — more tickets means higher proportion - When a job runs is **random** — may win multiple lotteries in a row or lose many ### Implementation ```c int counter = 0; int winner = getrandom(0, totaltickets); node_t *current = head; while (current) { counter += current->tickets; if (counter > winner) break; current = current->next; } // current is the winner ``` > [!example] Example > Jobs: A(1), B(1), C(100), D(200), E(100) — totaltickets = 402 > - `winner = 50` → counter passes 50 at Job C → **C runs** > - `winner = 350` → counter passes 350 at Job E → **E runs** > - `winner = 0` → counter passes 0 at Job A → **A runs** --- ## Summary **Scheduling Policies:** FIFO, SJF, STCF, RR, MLFQ, Lottery - Understand **workload characteristics** like arrival, CPU, I/O - Scope out goals with **metrics** (turnaround time, response time) - **Trade-offs** based on goals and metrics (RR vs. STCF) - Past behavior is a good predictor of future behavior (MLFQ) - Proportional-share scheduling (Lottery) provides fairness --- ## Related - **Navigation:** [[lecture-02|← Lecture 2]] · [[lecture-04|Lecture 4 →]] - **Exam:** [[exams/midterm-1/index|Midterm 1]] - **Course:** [[README|CS 537 Map of Content]]